package cn.buk.qms.service;

import cn.buk.api.airskyhk.dto.*;
import cn.buk.api.eotms.dto.FlightOrderCreateRequest;
import cn.buk.api.eotms.dto.FlightOrderResponse;
import cn.buk.api.eotms.service.FlightApiService;
import cn.buk.common.Constant;
import cn.buk.common.JsonResult;
import cn.buk.common.dto.CommonDto;
import cn.buk.common.dto.eterm.PnrDto;
import cn.buk.common.dto.eterm.PnrFlightDto;
import cn.buk.common.dto.eterm.PnrPassengerDto;
import cn.buk.common.flight.dto.FlightInfoDto;
import cn.buk.common.util.FlightTicketUtil;
import cn.buk.qms.OrderUtil;
import cn.buk.tms.common.dto.base.BasePassengerDto;
import cn.buk.tms.dao.DeliveryNoteDao;
import cn.buk.tms.dao.DetrDao;
import cn.buk.tms.dto.*;
import cn.buk.tms.entity.config.EnterpriseConfig;
import cn.buk.tms.entity.misc.*;
import cn.buk.tms.entity.order.OrderAttachment;
import cn.buk.tms.entity.order.OrderNoInfo;
import cn.buk.tms.exception.BaseException;
import cn.buk.tms.sc.SearchFlightOrderCriteria;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.tms.common.dto.base.BaseOrderDto;
import cn.buk.tms.common.dto.base.BasePriceDto;
import cn.buk.tms.common.dto.flight.BaseChangeOrderFlightDto;
import cn.buk.tms.common.dto.flight.BaseFlightOrderPassengerDto;
import cn.buk.tms.dto.LoginInfoDto;
import cn.buk.tms.dto.PassengerDto;
import cn.buk.tms.dto.PassengerIdInfoDto;
import cn.buk.tms.dto.SmsDetailDto;
import cn.buk.tms.entity.base.Airline;
import cn.buk.tms.entity.base.Airport;
import cn.buk.tms.entity.bill.BillFlightTicket;
import cn.buk.tms.entity.order.flight.*;
import cn.buk.tms.entity.order.vas.VasOrder;
import cn.buk.tms.flight.dao.KeyCustomerPolicyDao;
import cn.buk.tms.flight.dto.*;
import cn.buk.tms.flight.dto.response.ChangeOrderResponseDto;
import cn.buk.tms.flight.dto.response.FlightOrderResponseDto;
import cn.buk.tms.flight.dto.response.FlightOrderTicketDto;
import cn.buk.tms.flight.dto.response.RefundOrderResponseDto;
import cn.buk.tms.flight.request.FlightOrderRequestDto;
import cn.buk.tms.flight.service.EtermService;
import cn.buk.tms.flight.util.FlightOrderUtil;
import cn.buk.tms.tms.response.*;
import cn.buk.tms.util.IdUtil;
import cn.buk.common.util.CheckUtil;
import cn.buk.common.util.DateUtil;
import cn.buk.common.util.FlightUtil;
import cn.buk.tms.vas.dto.response.VasOrderResponseDto;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

import static cn.buk.common.Constant.*;
import static cn.buk.common.JsonResult.STATUS_OK;
import static cn.buk.tms.entity.config.EnterpriseConfig.*;
import static cn.buk.tms.common.constant.TmsConstant.*;
import static cn.buk.tms.common.constant.TmsOrderConstant.*;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @author yfdai
 * 2015-3-19
 */
@Component
public class FlightOrderServiceImpl extends BaseFlightOrderService implements FlightOrderService {

  private static final Logger logger = LogManager.getLogger(FlightOrderServiceImpl.class);

  @Autowired
  public FlightOrderServiceImpl(DeliveryNoteDao deliveryNoteDao,
                                DetrDao detrDao,
                                EtermService etermService,
                                KeyCustomerPolicyDao keyCustomerPolicyDao) {
    this.deliveryNoteDao = deliveryNoteDao;
    this.detrDao = detrDao;
    this.etermService = etermService;
    this.keyCustomerPolicyDao = keyCustomerPolicyDao;
  }

  private final DeliveryNoteDao deliveryNoteDao;

  private final DetrDao detrDao;

  private final EtermService etermService;

  private final KeyCustomerPolicyDao keyCustomerPolicyDao;



  /**
   * 校验机票订单的数据是否符合要求
   */
  private JsonResult validateFlightOrder(FlightOrderRequestDto dto, final int validationMode) {
    // 判断客户信息
    if (dto.getCustomerId() < 0 || !isValidCustomer(dto.getEnterpriseId(), dto.getCustomerId())) {
      return JsonResult.createJsonResult(-2, "请录入准确的客户信息");
    }
    if (dto.getFlights().isEmpty()) {
      return JsonResult.createJsonResult(-5, "航班信息不能为空");
    }

    JsonResult jsonResult = doValidateFlightOrderFlights(dto, validationMode);
    if (jsonResult != null) {
      return jsonResult;
    }

    if (!dto.getPassengers().isEmpty()) {
      jsonResult = doValidateFlightOrderPassengers(dto, validationMode);
      if (jsonResult != null) {
        return jsonResult;
      }
    }

    if (dto.getItineraryType() > 0 && dto.getItineraryMailingAddress().length() < 5) {
      //需要邮寄行程单或发票，则需要录入地址
      return JsonResult.createJsonResult(-6, "需要录入行程单的邮寄地址");
    }

    jsonResult = doValidateFlightOrderAdultPrice(dto);
    if (jsonResult != null) {
      return jsonResult;
    }

    jsonResult = doValidateFlightOrderChildPrice(dto);
    if (jsonResult != null) {
      return jsonResult;
    }


    //校验传过来的值
    dto.calcCost(); //计算成本
    double profit1 = Math.abs(dto.getTotalAmount() - dto.getTotalCost() - dto.getTotalProfit());

    if (profit1 > 0.001) {
      return JsonResult.createJsonResult(-8, "利润值校验失败，请检查");
    }

    //校验大客户代码的长度
    if (StringUtils.trimToEmpty(dto.getKeyCustomerNo()).length() > 50) {
      return JsonResult.createJsonResult(-9, "大客户代码长度大了些");
    }

    //校验编码长度
    if (StringUtils.trimToEmpty(dto.getPnrNo()).length() > 10) {
      return JsonResult.createJsonResult(-10, "编码最大10个字符");
    }

    return null;
  }

  private JsonResult doValidateFlightOrderAdultPrice(FlightOrderRequestDto dto) {
    if (dto.getAdultPrice() != null) {
      BasePriceDto priceDto = dto.getAdultPrice();
      if (priceDto.getParValue() < 0.01 && priceDto.getPrice() > 0) {
        return JsonResult.createJsonResult(-8, "成人销售价不能为0, 请刷新页面再试");
      }
    }

    return null;
  }

  private JsonResult doValidateFlightOrderChildPrice(FlightOrderRequestDto dto) {
    if (dto.getChildPrice() != null) {
      BasePriceDto priceDto = dto.getChildPrice();
      if (priceDto.getParValue() < 0.01 && priceDto.getPrice() > 0) {
        return JsonResult.createJsonResult(-8, "儿童销售价不能为0, 请刷新页面再试");
      }
    }

    return null;
  }

  private JsonResult doValidateFlightOrderPassengers(FlightOrderRequestDto dto, final int validationMode) {
    final Set<String> mobileSet = new HashSet<>();
    for (PnrPassengerDto psg : dto.getPassengers()) {
      JsonResult jsonResult = validatePassengerInfo(psg, mobileSet, validationMode);
      if (jsonResult != null) {
        return jsonResult;
      }
    }
    if (validationMode == 1 && mobileSet.size() != dto.getPassengers().size()) {
      return JsonResult.createJsonResult(-5, "手机号数量不符合要求");
    }

    return null;
  }

  private JsonResult doValidateFlightOrderFlights(FlightOrderRequestDto dto, final int validationMode) {
    for (PnrFlightDto flt : dto.getFlights()) {
      FlightInfoDto info = flt.getFlight();

      JsonResult jsonResult = validateFlightInfo(info, validationMode);
      if (jsonResult != null) {
        return jsonResult;
      }
    }

    return null;
  }

  /**
   * 创建机票订单
   *
   * @param enterpriseId 企业id
   * @param username     用户名
   * @param dto          FlightOrderDto
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult createFlightOrder(final int enterpriseId, final String username,
                                      FlightOrderRequestDto dto) {
    //控制提交频率
    final String key = username + "_" + enterpriseId + "_create_flight_order_frequency";
    final int apiCallCount =  redisDao.incrApiCallCount(key, 5, 1);
    if ( apiCallCount == 0) {
      return JsonResult.createJsonResult(-1, "操作太频繁");
    }

    // DTO中的内容验证
    final int orderValidateMode = enterpriseDao.getEnterpriseConfig(enterpriseId, EnterpriseConfig.TMS_ORDER_VALIDATE_MODE, 0);

    dto.setEnterpriseId(enterpriseId);
    JsonResult validateResult = validateFlightOrder(dto, orderValidateMode);
    if (validateResult != null) {
      return validateResult;
    }

    //供应商
    Supplier supplier = enterpriseDao.getSupplierById(enterpriseId, dto.getSupplierId());

    // 付款方式
    PaymentMethod paymentMethod = enterpriseDao.getPaymentMethodById(enterpriseId, dto.getPaymentMethodId());

    // 判断大客户代码是否准确
    if (StringUtils.isNotBlank(dto.getKeyCustomerNo())) {
      var policies = keyCustomerPolicyDao.searchFlightKeyCustomerPolicyByKeyCustomerNo(enterpriseId, dto.getKeyCustomerNo());
      if (policies.isEmpty()) {
        return JsonResult.createJsonResult(-4, "未匹配上大客户政策");
      }
    }

    if (StringUtils.trimToEmpty(dto.getTktlTime()).trim().length() == 4) {
      // 时间放到etdzDate一起
      dto.setTktlDate(OrderUtil.getTktlDate(dto.getTktlDate(), dto.getTktlTime()));
    }

    // 将DTO中的内容转化到FlightOrder中

    FlightOrder info = FlightOrderUtil.convertTo(dto);

    info.setCreateDate(DateUtil.getCurDate());
    info.setOperator(username);

    if (dto.getCustomerId() == 0) {
      //散客
      info.setCustomerType(0);
    } else {
      info.setCustomerType(1);
      info.setCustomer(customerDao.getCustomerById(dto.getCustomerId()));
    }
    info.setSupplier(supplier);
    info.setPaymentMethod(paymentMethod);


    if (info.getTktl() == null) {
      // 默认 两小时内出票
      //TODO 需要考虑时区？
      Date tktl = DateUtil.addMinutes(DateUtil.getCurDateTime(), 120);
      info.setTktl(tktl);
    }
    info.setEtdzDate(null);

    processOrderAirportName(info);


    //处理到达时间+1的问题
    processFlightArriveTime(info);

    //生成机票订单号
    final String orderNo = orderNoGenerator.generateOrderNo(enterpriseId, OrderNoInfo.ORDER_FLIGHT);
    info.setOrderNo(orderNo);

    int retCode;
    try {
      retCode = flightOrderDao.createOrder(info, username);
    } catch (RuntimeException ex) {
      ex.printStackTrace();
      return JsonResult.createJsonResult(-100, ex.getMessage());
    }

    if (retCode > 0) {
      postProcessFlightOrderCreated(enterpriseId, username, info);
    }

    // 有配送需求，保存到配送单
    if (retCode > 0 && info.getItineraryType() > 0) {
      DeliveryNote dn = new DeliveryNote();
      dn.setEnterpriseId(enterpriseId);
      dn.setOperator(username);

      dn.setOrderNo(orderNoGenerator.generateOrderNo(enterpriseId, OrderNoInfo.DELIVERY_NOTE));

      dn.setDeliveredOrderType(OrderNoInfo.ORDER_FLIGHT);
      dn.setDeliveredOrderNo(info.getOrderNo());

      dn.setDeliveredContent(FlightOrderUtil.getItineraryTypeDesc(info.getItineraryType()));
      dn.setDeliveryDate(info.getDeliveryDate());
      dn.setReceiver(info.getItineraryRecipient());
      dn.setMobile(info.getItineraryRecipientMobile());
      dn.setAddress(info.getItineraryMailingAddress());
      dn.setRemark(info.getItineraryMailingMemo());

      try {
        deliveryNoteDao.createDeliveryNote(dn);
      } catch (RuntimeException ex) {
        logger.error(ex.getMessage());
      }
    }

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 机票订单创建后的自动处理操作
   */
  private void postProcessFlightOrderCreated(final int enterpriseId, final String username, final FlightOrder info) {
//判断该企业是否不需要走订单处理流程，需要尽量一步完成
    String oneStepConfig = doGetEntCfg(enterpriseId, EnterpriseConfig.TMS_ONE_STEP_CREATE_ORDER);
    if ("0".equalsIgnoreCase(oneStepConfig) || oneStepConfig.length() == 0) {
      // 判断该租户是否只有一个用户
      List<User> users = userDao.searchEmployees(enterpriseId);
      // 如果只有一个用户，则也一步完成
      if (users.size() == 1) {
        oneStepConfig = "1";
      }
    }

    if ("1".equalsIgnoreCase(oneStepConfig)) {
      finishFlightOrderOneStep(enterpriseId, username, info);
    }

    // 校验订单中的乘机人姓名和证件号
    threadPool.execute(() -> validateFlightOrderPassengerInfo(info));
    // 校验订单中的乘机人和航班信息是否重复
    threadPool.execute(() -> validateFlightOrderDuplicate(info));
  }

  private void validateFlightOrderDuplicate(FlightOrder orderInfo) {
    final int enterpriseId = orderInfo.getEnterpriseId();

    if (passengerDao == null || basicDataDao == null) {
      return;
    }

    var flightOrderInfo = flightOrderDao.getOrderDetailById(orderInfo.getId());

    //用航班号和日期来查询订单数
    final String dport = flightOrderInfo.getFlights().get(0).getFlight().getDport();
    final String aport = flightOrderInfo.getFlights().get(0).getFlight().getAport();
    final String flightNo = flightOrderInfo.getFlights().get(0).getFlight().getFlightNo();
    final String ddate = flightOrderInfo.getFlights().get(0).getFlight().getDepartureDate();
    var sc = new SearchFlightOrderCriteria();
    sc.setDepartureDate(ddate);
    sc.setFlightNo(flightNo);
    var existedOrders = flightOrderDao.searchOrder(enterpriseId, null, sc, false, null);
    logger.info(existedOrders.size());
    for(var oldOrder: existedOrders) {
      if (oldOrder.getOrderNo().equalsIgnoreCase(flightOrderInfo.getOrderNo())) {
        continue;
      }

      //通过编码来校验是否重复
      if (oldOrder.getPnrNo() != null && oldOrder.getPnrNo().equalsIgnoreCase(flightOrderInfo.getPnrNo())) {
        final String content = String.format("机票订单(%s, %s, %s%s %s %s)可能与另一机票订单(%s, %s)重复.",
                flightOrderInfo.getOrderNo(), flightOrderInfo.getPnrNo(), dport, aport, ddate, flightNo,
                oldOrder.getOrderNo(), oldOrder.getPnrNo());
        logger.info(content);
        sendFlightOrderAlertToWwAppChat(enterpriseId, content);
        continue;
      }

      //通过乘机人来校验是否重复
      for (var psgInfo1 : flightOrderInfo.getPassengers()) {
        for(var psgInfo0: oldOrder.getPassengers()) {
          if (psgInfo0.getName().equalsIgnoreCase(psgInfo1.getName())
                  && psgInfo0.getIdType() == psgInfo1.getIdType()
                  && psgInfo0.getIdNo().equalsIgnoreCase(psgInfo1.getIdNo())
          ) {
            final String content = String.format("订单(%s, %s, %s%s %s %s)可能与另一订单(%s)有重复.",
                    flightOrderInfo.getOrderNo(), psgInfo1.getName(), dport, aport, ddate, flightNo,
                    oldOrder.getOrderNo());
            logger.info(content);
            sendFlightOrderAlertToWwAppChat(enterpriseId, content);
          }
        }
      }
    }
  }

  private void processFlightArriveTime(FlightOrder info) {
    for (FlightOrderFlight flight : info.getFlights()) {
      final cn.buk.tms.entity.misc.FlightInfo fltInfo = flight.getFlight();
      final String atime = fltInfo.getArrivalTime();
      if (FlightOrderUtil.validateFlightTime(atime) && atime.length() == 6 && atime.contains("+1")) {
        try {
          Date ddate = DateUtil.convertToDate(fltInfo.getDepartureDate(), DATE_YYYY_MM_DD);
          Date adate = DateUtil.addDays(ddate, 1);
          fltInfo.setArrivalDate(DateUtil.formatDate(adate, DATE_YYYY_MM_DD));
          fltInfo.setArrivalTime(atime.substring(0, 4));
        } catch (ParseException e) {
          e.printStackTrace();
        }
      }
    }

  }

  private boolean isValidCustomer(int enterpriseId, int customerId) {
    if (customerId < 0) {
      return false;
    } else if (customerId > 0) {
      var customer = customerDao.getCustomerById(customerId);
      return customer != null && customer.getEnterpriseId() == enterpriseId;
    }

    return true;
  }

  /**
   * 校验订单中的乘机人姓名和证件号是否和数据库中已经的匹配
   */
  private void validateFlightOrderPassengerInfo(FlightOrder flightOrderInfo) {
    final int enterpriseId = flightOrderInfo.getEnterpriseId();
    final String orderNo = flightOrderInfo.getOrderNo();

    if (passengerDao == null || basicDataDao == null) {
      return;
    }

    for (FlightOrderPassenger psgInfo : flightOrderInfo.getPassengers()) {
      final String psgName = psgInfo.getName().trim();
      final String idNo = psgInfo.getIdNo().trim();
      final int idType = psgInfo.getIdType();
      final int customerId = flightOrderInfo.getCustomer() == null ? 0 : flightOrderInfo.getCustomer().getId();

      List<Passenger> psgList = passengerDao.searchByName(enterpriseId, customerId, psgName);
      if (psgList.isEmpty()) {
        continue;
      }

      //ID证件是否能找到匹配一致的
      checkPassengerIdMatched(psgName, idType, idNo, psgList, flightOrderInfo, psgInfo);
    }

    //校验机场代码是否都存在于系统中
    Set<String> airportSet = new HashSet<>();
    for (FlightOrderFlight flt : flightOrderInfo.getFlights()) {
      airportSet.add(flt.getFlight().getDepartureAirport());
      airportSet.add(flt.getFlight().getArrivalAirport());
    }

    for (String threeCode : airportSet) {
      Airport airport = basicDataDao.getAirport(threeCode);
      if (airport == null) {
        String content = "订单(" + orderNo + ")中的机场代码(" + threeCode + ")不存在于机场代码表中，请检查或联系管理员";
        doSendAlert(enterpriseId, content, null, EntDepartment.DEPT_CUSTOMER_SERVICE);
      }
    }

    //校验航司代码是否存在静态文件中
    Set<String> carrierSet = new HashSet<>();
    for (FlightOrderFlight flt : flightOrderInfo.getFlights()) {
      carrierSet.add(flt.getFlight().getFlightNo().substring(0, 2));
    }

    for (String twoCode : carrierSet) {
      List<Airline> airlines = basicDataDao.searchAirlines(twoCode);
      if (airlines.isEmpty()) {
        String content = "订单(" + orderNo + ")中的航司代码(" + twoCode + ")不存在于航司代码表中，请检查或联系管理员";
        doSendAlert(enterpriseId, content, null, EntDepartment.DEPT_CUSTOMER_SERVICE);
      }
    }
  }

  /**
   * 校验乘机人证件是否匹配
   *
   * @param psgList 乘机人列表中匹配到的已有的乘机人信息
   * @param psgInfo 机票订单中的乘机人信息
   */
  private void checkPassengerIdMatched(final String psgName, final int idType, final String idNo,
                                       List<Passenger> psgList, FlightOrder orderInfo, FlightOrderPassenger psgInfo) {
    StringBuilder oldIdNo = new StringBuilder();

    boolean isOk = true;

    for (Passenger psg0 : psgList) {
      PassengerDto psgDto = PassengerDto.createBy(psg0);

      isOk = doCheckIdMatchedPerPassenger(idType, idNo, psgDto, psgInfo, oldIdNo);

      if (isOk) {
        break;
      }
    }

    if (!isOk) {
      // 提示
      final String title = "证件信息校对";
      final String content = String.format("机票订单(%s)的乘机人(%s)的证件号(%s)与以往保存的(%s)不匹配，请仔细检查", orderInfo.getOrderNo(), psgName, idNo, oldIdNo);
      final int enterpriseId = orderInfo.getEnterpriseId();
      doSendFlightOrderAlertByTaskCardMsg(enterpriseId, orderInfo.getId(), title, content, EntDepartment.DEPT_CUSTOMER_SERVICE + "");
    }
  }

  private boolean doCheckIdMatchedPerPassenger(final int idType, final String idNo, PassengerDto psgDto,
                                               FlightOrderPassenger psgInfo, StringBuilder oldIdNo) {
    boolean isOk = true;
    for (PassengerIdInfoDto idInfoDto : psgDto.getIdInfos()) {
      final String idNo0 = idInfoDto.getIdNo();
      if (idType == idInfoDto.getIdType()) {
        if (idNo.equalsIgnoreCase(idNo0)) {
          //证件类型、证件号都匹配
          //保存需要的信息到机票订单乘机人中
          flightOrderDao.updateFlightOrderPassengerVipAndId(psgInfo.getId(), psgDto.getId(), psgDto.getVipLevel());
          isOk = true;
          break;
        } else if (idType == ID_TYPE_NI && !idNo.substring(0, 6).equalsIgnoreCase(idNo0.substring(0, 6))) {
          //身份证，前六位不匹配，则认为证件号匹配（同名的乘机人而已）
          isOk = true;
        } else {
          oldIdNo.append(idNo0).append(", ");
          isOk = false;
        }
      }
    }

    return isOk;
  }

  /**
   * 一步完成订单
   */
  private void finishFlightOrderOneStep(final int enterpriseId, final String username,
                                        final FlightOrder info) {

    //提交开票
    JsonResult jsonResult = this.submitFlightOrder(enterpriseId, username, info.getId());
    if ("ER".equalsIgnoreCase(jsonResult.getStatus())) {
      return;
    }

    //我来开票
    jsonResult = this.toTicketFlightOrder(enterpriseId, username, info.getId());
    if ("ER".equalsIgnoreCase(jsonResult.getStatus())) {
      return;
    }

    //回填票号
    jsonResult = this
            .updateFlightOrderTicketNo(enterpriseId, username, info.getId(), info.getPassengers());

    if ("ER".equalsIgnoreCase(jsonResult.getStatus())) {
      return;
    }

    //订单完成
    this.finishFlightOrder(enterpriseId, username, info.getId(), null);
  }





  @Override
  public List<FlightOrderResponseDto> searchFlightOrders(final int enterpriseId, final int userId, final String username,
                                                         final SearchFlightOrderCriteria sc) {
    Set<String> roles = userDao.loadUserRoles(userId);

    List<FlightOrder> flightOrders;

    List<Integer> allowedCustomerIds = searchAllowedCustomerIds(enterpriseId, userId);

    if (roles.size() == 1 && roles.contains(RoleInfo.ROLE_TICKETER)) {
      //如果只有出票员的角色，那么只能看自己出票的，和没有认领的
      flightOrders = flightOrderDao.searchOrder(enterpriseId, username, sc, true, allowedCustomerIds);

      flightOrders = flightOrders.stream()
              .filter(t -> t.getStatus() != TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED)
              .collect(Collectors.toList());
    } else if (roles.size() == 1 && roles.contains(RoleInfo.ROLE_OPERATOR)) {
      //仅有“客服”角色, 判断是否开启了“仅查看自己订单”
      if (isOnlySeeOwn(enterpriseId, userId)) {
        sc.setOp1(username);
      }
      flightOrders = flightOrderDao.searchOrder(enterpriseId, username, sc, false, allowedCustomerIds);
    } else {
      flightOrders = flightOrderDao.searchOrder(enterpriseId, username, sc, false, allowedCustomerIds);
    }

    if (flightOrders == null) {
      flightOrders = new ArrayList<>();
    }

    for (FlightOrder order : flightOrders) {
      FlightOrderUtil.clean(order);
    }

    //DONE 判断是否启用业务规则，隐藏客户的企业名称，用三个星号替代 2019-12-13
    final boolean needHideCustomerName = isNeedHideCustomerName(enterpriseId, userId);
    User user = userDao.getUserById(userId);
    for (FlightOrder order : flightOrders) {
      maskCustomerName(order.getCustomer(), order.getOperator(), user.getUsername(), needHideCustomerName);
    }

    List<FlightOrderResponseDto> dtos = new ArrayList<>();
    for (FlightOrder o : flightOrders) {
      dtos.add(FlightOrderResponseDto.createByFlightOrder(o));
    }

    return dtos;
  }

  @Override
  public CommonDto<FlightOrderResponseDto> searchFlightOrders2(int enterpriseId, int userId, String username, SearchFlightOrderCriteria sc) {
    var datas = this.searchFlightOrders(enterpriseId, userId, username, sc);

    CommonDto<FlightOrderResponseDto> commonDto = new CommonDto<>();
    commonDto.setDataList(datas);
    commonDto.setPage(sc.getPage());

    return commonDto;
  }

  /**
   * 搜索需要送票的机票订单
   */
  @Override
  public List<FlightOrderResponseDto> searchNeedDeliveryFlightOrders(int enterpriseId, int userId,
                                                                     SearchFlightOrderCriteria sc) {
    List<FlightOrder> flightOrders = flightOrderDao
            .searchNeedDeliveryFlightOrders(enterpriseId, sc);

    for (FlightOrder order : flightOrders) {
      FlightOrderUtil.clean(order);
    }

    final boolean needHideCustomerName = isNeedHideCustomerName(enterpriseId, userId);
    User user = userDao.getUserById(userId);
    for (FlightOrder order : flightOrders) {
      maskCustomerName(order.getCustomer(), order.getOperator(), user.getUsername(), needHideCustomerName);
    }

    List<FlightOrderResponseDto> dtos = new ArrayList<>();
    for (FlightOrder o : flightOrders) {
      dtos.add(FlightOrderResponseDto.createByFlightOrder(o));
    }

    return dtos;
  }

  @Override
  public FlightOrderResponseDto getFlightOrderDetail(final int enterpriseId, final int userId, final int orderId) {
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);

    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return FlightOrderResponseDto.create(404, "不存在此订单或无权限");
    }

    //考虑分组，查看该客户是否属于当前用户的分组
    List<Integer> allowedCustomerIds = searchAllowedCustomerIds(enterpriseId, userId);
    if (!allowedCustomerIds.isEmpty()
            && flightOrder.getCustomer() != null
            && !allowedCustomerIds.contains(flightOrder.getCustomer().getId())
    ) {
      //不在当前用户所允许查看的客户内
      return FlightOrderResponseDto.create(404, "不存在此订单或无权限");
    }

    //是否要对当前用户隐藏客户名称
    if (userId > 0 && isNeedHideCustomerName(enterpriseId, userId)) {
      User user = userDao.getUserById(userId);
      maskCustomerName(flightOrder.getCustomer(), flightOrder.getOperator(), user.getUsername(), true);
    }

    FlightOrderResponseDto dto = FlightOrderResponseDto.createByFlightOrder(flightOrder);

    //获取子订单
    doGetSubOrder(enterpriseId, dto);

    //取得相关的 短信
    List<SmsDetail> smsDetails = smsDao.searchSmsByOrderNo(enterpriseId, flightOrder.getOrderNo());
    for (SmsDetail sms : smsDetails) {
      SmsDetailDto smsDto = new SmsDetailDto();
      BeanUtils.copyProperties(sms, smsDto);

      dto.getSmsDetails().add(smsDto);
    }

    //获取相关的 退票
    List<FlightRefundOrder> refundOrders = refundOrderDao.searchByFlightOrderNo(enterpriseId, flightOrder.getOrderNo());
    for (FlightRefundOrder refundOrder : refundOrders) {
      dto.getRefunds().add(RefundOrderResponseDto.createBy(refundOrder));
    }

    //获取相关的 改签
    List<FlightChangeOrder> changeOrders = changeOrderDao.searchByFlightOrderNo(enterpriseId, flightOrder.getOrderNo());
    for (FlightChangeOrder changeOrder : changeOrders) {
      dto.getChanges().add(ChangeOrderResponseDto.createdBy(changeOrder));
    }

    //获取相关的 服务
    List<VasOrder> vasOrders = vasOrderDao.searchByFlightOrderId(enterpriseId, flightOrder.getId());
    for (VasOrder vasOrder : vasOrders) {
      dto.getServices().add(VasOrderResponseDto.createByVasOrder(vasOrder));
    }

    //获取配送相关的订单
    if (flightOrder.getDeliveryNoteNo() != null) {
      List<DeliveryNote> notes = deliveryNoteDao.searchDeliveryNoteByOrderNo(enterpriseId, flightOrder.getDeliveryNoteNo());
      for (DeliveryNote deliveryNote : notes) {
        dto.getDeliveries().add(DeliveryNoteResponseDto.createBy(deliveryNote));
      }
    }

    //获取相关的附件内容
    List<OrderAttachment> attachments = flightOrderDao.searchAttachments(enterpriseId, flightOrder.getId());
    processAttachments(enterpriseId, attachments, dto.getAttachments());

    doGetTickets(dto);
    for (var t : dto.getTickets()) {
      t.setDportName(getAirportName(t.getDport()));
      t.setAportName(getAirportName(t.getAport()));
    }

    dto.processPassengerTicketStatus();

    return dto;
  }

  private String getAirportNameEn(String threeCode) {
    Airport airport = basicDataDao.getAirport(threeCode);
    return airport == null ? null : airport.getNameEN();
  }

  private void doGetSubOrder(final int enterpriseId, final FlightOrderResponseDto dto) {
    if (dto.getBlanketOrderId() != null) {
      //有对应的总订单，查找是否有子订单
      List<BlanketOrder> subOrders = flightOrderDao.searchSubOrders(enterpriseId, dto.getBlanketOrderId());
      if (!subOrders.isEmpty()) {
        for (BlanketOrder subOrder : subOrders) {
          if (subOrder.getActualOrderType() == TmsOrderConstant.ORDER_TYPE_INSURANCE) {
            //保险订单
            VasOrder vasOrder = vasOrderDao.getOrderById(subOrder.getActualOrderId());

            BaseOrderDto baseOrderDto = new BaseOrderDto();
            baseOrderDto.setId(vasOrder.getId());
            baseOrderDto.setOrderType(subOrder.getActualOrderType());
            baseOrderDto.setOrderNo(vasOrder.getOrderNo());
            baseOrderDto.setOrderStatus(vasOrder.getStatus());
            baseOrderDto.setTotal(vasOrder.getTotal());

            dto.getSubOrders().add(baseOrderDto);

            //将子订单的价格加入到totalAmount中
            dto.setTotalAmount(dto.getTotalAmount() + baseOrderDto.getTotal());
          }
        }
      }
    }

  }

  /**
   * 将DETR信息的改签结果合并到机票订单
   */
  private void doGetTickets(final FlightOrderResponseDto dto) {
    if (dto.getTickets().isEmpty()) {
      return;
    }
    //将改签单的票号内容合并到dto.tickets中
    processTicketsChanged(dto);

    //将退票单的内容合并到dto.tickets
    processTicketRefunded(dto);

    var list = dto.getTickets().stream().sorted(
            Comparator.comparing(FlightOrderTicketDto::getPassengerName)
                    .thenComparing(FlightOrderTicketDto::getDdate)
                    .thenComparing(FlightOrderTicketDto::getDtime)
    ).collect(Collectors.toList());

    dto.setTickets(list);
  }

  /**
   * 将改签订单的内容合并到机票订单的票号内容中
   */
  private void processTicketsChanged(final FlightOrderResponseDto dto) {
    for (ChangeOrderResponseDto changeDto : dto.getChanges()) {
      processTicketChanged(dto, changeDto);
    }
  }

  /**
   * 合并改签单中的票号信息到机票订单的票号信息中。
   *
   * @param dto       机票订单
   * @param changeDto 改签单
   */
  private void processTicketChanged(final FlightOrderResponseDto dto, final ChangeOrderResponseDto changeDto) {
    for (BaseChangeOrderFlightDto flt : changeDto.getFlights()) {
      if (flt.getFltType() == FLIGHT_BEFORE_CHANGED) {
        //忽略改签单中标记为改签前的航班
        continue;
      }
      processTicketFlightChanged(dto, changeDto, flt);
    }
  }

  /**
   * 合并改签单中的航班信息到机票订单
   *
   * @param changeDto 改签单
   * @param flt       改签单中包含的改签后的航班信息
   */
  private void processTicketFlightChanged(final FlightOrderResponseDto dto, final ChangeOrderResponseDto changeDto,
                                          BaseChangeOrderFlightDto flt) {
    var t = new FlightOrderTicketDto();
    t.setPassengerName(changeDto.getPsgName());

    final String ticketNo = changeDto.getNewTicketNo() == null ?
            changeDto.getBalanceCode() + "-" + changeDto.getTicketNo()
            :
            changeDto.getNewTicketNo();

    t.setTicketNo(ticketNo);

    t.setDport(flt.getDport());
    t.setAport(flt.getAport());
    t.setFlightNo(flt.getFlightNo());
    t.setDdate(DateUtil.formatDate(flt.getDdate(), DATE_YYYY_MM_DD));
    t.setSubclass(flt.getSubclass());
    t.setDtime(flt.getDtime());
    t.setAtime(flt.getAtime());
    t.setFltType(flt.getFltType());

    if (changeDto.getDetrRefreshStatus() == 8 || changeDto.getDetrRefreshStatus() == 1) {
      //DETR状态已更新
      processChangedTicketDetrInfo(dto.getEnterpriseId(), t);
    }

    dto.getTickets().add(t);

    applyFlightChangedStatus(dto.getTickets(), changeDto.getPsgName(), flt);
  }

  private void processChangedTicketDetrInfo(final int enterpriseId, FlightOrderTicketDto t) {
    var detrInfo = detrDao.getByTicketNo(enterpriseId, t.getTicketNo().replace("-", ""));
    if (detrInfo == null) {
      return;
    }
    if ("2".equalsIgnoreCase(detrInfo.getIsUsed())) {
      t.setTicketStatus(FLIGHT_TICKET_STATUS_USED);
      return;
    }
    for (var detrFlt : detrInfo.getFlights()) {
      if (detrFlt.getDport().equalsIgnoreCase(t.getDport())
              && detrFlt.getAport().equalsIgnoreCase(t.getAport())
              && detrFlt.getSubClass().equalsIgnoreCase(t.getSubclass())
      ) {
        if (DETR_EXCHANGED.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_EXCHANGED);
        } else if (DETR_REFUNDED.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_REFUNDED);
        } else if (DETR_USED_FLOWN.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_USED);
        } else if (DETR_OPEN_FOR_USE.equalsIgnoreCase(detrFlt.getTicketStatus())) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_OPEN_FOR_USE);
        }
      }
    }
  }

  /**
   * 将退票单的内容合并到机票订单的票号内容中
   */
  private void processTicketRefunded(final FlightOrderResponseDto dto) {
    for (var refundDto : dto.getRefunds()) {
      if (refundDto.getStatus() != FlightRefundOrder.ORDER_STATUS_DONE) {
        continue;
      }
      for (var flt : refundDto.getFlights()) {
        for (var t : dto.getTickets()) {
          if (t.getPassengerName().equalsIgnoreCase(refundDto.getPsgName())
                  && flt.getDport().equalsIgnoreCase(t.getDport())
                  && flt.getAport().equalsIgnoreCase(t.getAport())
                  && flt.getFlightNo().equalsIgnoreCase(t.getFlightNo())
                  && DateUtil.formatDate(flt.getDdate(), DATE_YYYY_MM_DD).equalsIgnoreCase(t.getDdate())
                  && flt.getDtime().equalsIgnoreCase(t.getDtime())
          ) {
            t.setTicketStatus(FLIGHT_TICKET_STATUS_REFUNDED);
          }
        }
      }
    }
  }

  private void applyFlightChangedStatus(List<FlightOrderTicketDto> tickets, String psgName, BaseChangeOrderFlightDto f0) {
    for (var t : tickets) {
      if (t.getFltType() == FLIGHT_AFTER_CHANGED) {
        continue;
      }
      if (t.getPassengerName().equalsIgnoreCase(psgName)) {
        //此处用城市代码进行判断，比如上海有2个机场
        if (FlightUtil.getCityCode(f0.getDport()).equalsIgnoreCase(FlightUtil.getCityCode(t.getDport()))
                && FlightUtil.getCityCode(f0.getAport()).equalsIgnoreCase(FlightUtil.getCityCode(t.getAport()))
        ) {
          t.setTicketStatus(FLIGHT_TICKET_STATUS_EXCHANGED);
        }
      }
    }
  }

  @Override
  public FlightOrderResponseDto getFlightOrderDetailByOrderNo(final int enterpriseId, int userId, final String orderNo) {
    FlightOrder flightOrder = flightOrderDao.getOrderDetailByOrderNo(enterpriseId, orderNo);

    return this.getFlightOrderDetail(enterpriseId, userId, flightOrder.getId());
  }

  /**
   * 修改机票订单中的价格信息
   *
   * @param enterpriseId 企业id
   * @param orderId      订单id
   * @param dto          要修改的价格内容
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_TICKETER})
  public JsonResult updateFlightOrderPrice(final int enterpriseId, String username, int orderId,
                                           FlightOrderRequestDto dto) {
    //检验服务费
    FlightOrder o = flightOrderDao.getOrderDetailById(orderId);
    o.setServiceCharge(dto.getAdultPrice().getServiceCharge());

    JsonResult v = validateFlightOrderServiceCharge(enterpriseId, o);
    if (v != null) {
      return v;
    }

    try {
      int retCode = flightOrderDao.updatePrice(enterpriseId, username, orderId, dto);
      return JsonResult.createJsonResult(retCode);
    } catch (RuntimeException ex) {
      ex.printStackTrace();
      return JsonResult.createJsonResult(-100, ex.getMessage());
    }
  }

  /**
   * 修改机票订单中的乘机人信息
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult updateFlightOrderPassenger(AuthInfo authInfo, FlightOrderPassengerDto psgDto) {
    int retCode = flightOrderDao.updatePassenger(authInfo, psgDto);
    return JsonResult.createJsonResult(retCode);
  }


  /**
   * 修改订单的pnr
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult updateFlightOrderPnr(int enterpriseId, String username, int orderId,
                                         String pnrNo) {
    pnrNo = pnrNo.trim().toUpperCase();
    if (pnrNo.length() < 5 || pnrNo.length() > 6) {
      return JsonResult.createJsonResult(-1, "编码长度不符合要求，应该介于5-6间");
    }

    int retCode = flightOrderDao.updatePnr(enterpriseId, username, orderId, pnrNo);
    return JsonResult.createJsonResult(retCode);
  }

  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult updateFlightOrderKeyCustomerNo(int enterpriseId, String username, int orderId, String keyCustomerNo) {
    int retCode = flightOrderDao.updateFlightOrderKeyCustomerNo(enterpriseId, username, orderId, keyCustomerNo);
    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 取消机票订单
   *
   * @param enterpriseId 当前用户的enterpriseId
   * @param username     当前用户的username
   * @param orderId      订单id
   * @param reasonCode   取消理由的代码
   * @param reasonDesc   取消理由
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult cancelFlightOrder(final int enterpriseId, final String username, final int orderId,
                                      final int reasonCode, final String reasonDesc) {
    //TODO 需要判断是否已收款，是否需要退款
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);

    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "没有此订单");
    }

    if (flightOrder.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED
            || flightOrder.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED
            || flightOrder.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED) {
      return JsonResult.createJsonResult(-2, "此订单无法取消");
    }

    int retCode;

    if (flightOrder.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING
            || flightOrder.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_SUBMITTED
            || flightOrder.getStatus() == FLIGHT_ORDER_STATUS_WAIT_TICKETING) {

      retCode = flightOrderDao.cancelFlightOrder(enterpriseId, username, orderId, reasonCode, reasonDesc);
      if (retCode > 0) {
        // 发送订单取消的消息 给订单创建者
        String wwUserId = userDao.getWwUserIdByUsername(enterpriseId, flightOrder.getOperator());
        doSendAlert(enterpriseId,
                "订单(" + flightOrder.getOrderNo() + ")已被取消，取消原因: " + reasonDesc + ".", wwUserId, 0);

        //取消外部订单
        cancelFlightOrderExternalOrderNo(enterpriseId, username, orderId);
      }

      return JsonResult.createJsonResult(retCode);
    } else if (flightOrder.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED) {
      // 只剩下一种状态了
      retCode = flightOrderDao
              .createCancelRequest(enterpriseId, username, orderId, reasonCode, reasonDesc);

      JsonResult jsonResult = JsonResult.createJsonResult(retCode);

      if (retCode > 0) {
        // 将取消请求发送给相应的人员，以提醒他操作
        // 发送订单取消的消息
        final String title = "取消机票开票申请";
        final String content = "订单(" + flightOrder.getOrderNo() + ")已申请取消，理由: " + reasonDesc + ".";
        doSendFlightOrderCancelRequestAlert(enterpriseId, orderId, retCode, title, content, EntDepartment.DEPT_TICKETING + "");

        jsonResult.setDesc("机票订单的取消请求已提交成功");
      }
      return jsonResult;
    } else {
      return JsonResult.createJsonResult(-999, "此处还没有实现");
    }
  }

  private void cancelFlightOrderExternalOrderNo(int enterpriseId, String username, int orderId) {
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder.getSupplier() == null || flightOrder.getSupplier().getIsBsp() != FLIGHT_AUTO_TICKETING_NDC_CZ) {
      return;
    }

    if (StringUtils.isEmpty(flightOrder.getExternalOrderNo()) || StringUtils.isEmpty(flightOrder.getPnrNo())) {
      return;
    }

    var rq = new OrderCancelRequest();

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_MOBILE));
    rq.getPayloadAttributes().setTransactionId("999");

    final String externalOrderNo = flightOrder.getExternalOrderNo();
    final String pnr = flightOrder.getPnrNo();
    rq.getRequest().setOrderNo(externalOrderNo);
    rq.getRequest().setPnr(pnr);

    var ndcService = createNdcService(enterpriseId);

    var rs = ndcService.cancelOrder(rq);

    if (SUCCESS.equalsIgnoreCase(rs.getStatus().getStatus())) {
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, "外部订单号(" + externalOrderNo + ", " + pnr + ")取消成功");
    } else {
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, "取消外部订单(" + externalOrderNo + ", " + pnr + ")时, " + rs.getStatus().getErrorMessage());
    }

  }

  /**
   * 提交订单开票
   */
  @Override
  @Secured({RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_ADMIN})
  public JsonResult submitFlightOrder(final int enterpriseId, final String username, final int orderId) {

    var flightOrder = flightOrderDao.getOrderDetailById(orderId);

    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-404, "无此订单");
    }

    if (flightOrder.getPassengers().isEmpty()) {
      return JsonResult.createJsonResult(-1, "该订单需要补充乘机人信息");
    }

    //国内机票，如果是客人自助下单，则需要由客户自主提交开票
    User user = userDao.getUserByUsername(flightOrder.getOperator());
    if (user.getParentEnterprise() == enterpriseId) {
      return JsonResult.createJsonResult(-1, "此订单需要由下单人提交");
    }
    if (user.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "无权提交该订单");
    }

    // 0 - 国内，1 - 国际
    final int intlTicket0 = flightOrder.getIntlTicket();
    final int intlTicket1 = checkIsDomOrIntl(flightOrder);

    if (intlTicket1 != intlTicket0) {
      logger.error(" is intl ticket({}, {})?", flightOrder.getOrderNo(), orderId);
      String wwUserId = userDao.getWwUserIdByUsername(enterpriseId, flightOrder.getOperator());
      doSendAlert(enterpriseId, "订单(" + flightOrder.getOrderNo() + ")是国际还是国内？", wwUserId,
              EntDepartment.DEPT_CUSTOMER_SERVICE);

      String temp = flightOrder.getRemark() + " (国际？" + intlTicket1 + ")";
      flightOrderDao.updateFlightOrderProperty(enterpriseId, "system-check", orderId, "remark", temp);
    }

    int retCode = flightOrderDao.submitFlightOrder(enterpriseId, username, orderId);
    if (retCode == FLIGHT_ORDER_STATUS_WAIT_TICKETING
            && FlightOrderUtil.isNotAutoTicketingSupplier(flightOrder)) {
      //订单待出票且非自动出票供应商, 发送通知给出票人员，有订单需要开票
      final String title = "机票订单通知";
      final String content = "机票订单(订单号：" + flightOrder.getOrderNo() + ", 订单日期：" + DateUtil.formatDate(flightOrder.getCreateDate(), "yyyy-MM-dd") + ")等待开票。";
      doSendFlightOrderAlertByTaskCardMsg(enterpriseId, orderId, title, content, EntDepartment.DEPT_TICKETING + "");
    }

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 检查机票是否是否为国内订单
   *
   * @return 0 - 国内订单，1-国际或港澳台订单
   */
  private int checkIsDomOrIntl(FlightOrder flightOrder) {
    Set<String> airportSet = new HashSet<>();

    List<Airport> airports = basicDataDao.searchAirportsByCountry("CN");
    for (Airport airport : airports) {
      airportSet.add(airport.getThreeCode());
    }

    int intlTicket1 = 0;
    for (FlightOrderFlight flt : flightOrder.getFlights()) {
      String port1 = flt.getFlight().getDepartureAirport();
      String port2 = flt.getFlight().getArrivalAirport();

      if (airportSet.contains(port1) && airportSet.contains(port2)) {
        continue;
      }

      intlTicket1 = 1;
    }

    return intlTicket1;
  }

  /**
   * 审批通过
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult approveFlightOrder(int enterpriseId, String username, int orderId) {
    int retCode = flightOrderDao.approveFlightOrder(enterpriseId, username, orderId);

    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public List<OperationLog> searchFlightOrderHistory(int enterpriseId, int orderId) {
    return flightOrderDao.searchHistory(enterpriseId, orderId);
  }

  /**
   * 认领开票
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult toTicketFlightOrder(final int enterpriseId, final String username, final int orderId) {
    //判断授信额度是否足够
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-404, "不存在");
    }

    if (flightOrder.getCustomer() != null && flightOrder.getCustomer().getCustomerEnterpriseId() > 0) {
      //该客户可以判断授信额度
      final int customerEnterpriseId = flightOrder.getCustomer().getCustomerEnterpriseId();
      if (isOverCreditLimit(enterpriseId, customerEnterpriseId, flightOrder.getTotalAmount())) {
        return JsonResult.createJsonResult(-100, "订单金额(" + flightOrder.getTotalAmount() + ")大于可用额度");
      }
    }

    //校验大客户政策开票是否符合年龄限制
    if (!isValidForAgeLimited(enterpriseId, username, flightOrder, 18, 65)) {
      //不符合年龄要求
      return JsonResult.createJsonResult(-1, "不符合大客户开票的年龄限制");
    }

    int retCode = flightOrderDao.toTicketFlightOrder(enterpriseId, username, orderId);
    return JsonResult.createJsonResult(retCode);
  }

  @Override
  @Secured({RoleInfo.ROLE_ADMIN})
  public JsonResult cancelClaimFlightOrder(int enterpriseId, String username, int orderId) {
    int retCode = flightOrderDao.cancelClaimFlightOrder(enterpriseId, username, orderId);
    return JsonResult.createJsonResult(retCode);
  }


  /**
   * 机票订单完成了
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult finishFlightOrder(final int enterpriseId, String username, int orderId,
                                      Date etdzDate) {
    // 检查票号在账单表中是否已经存在
    FlightOrder o = flightOrderDao.getOrderDetailById(orderId);
    for (FlightOrderPassenger psg : o.getPassengers()) {
      BillFlightTicket ticket = billService.searchFlightTicket(enterpriseId, psg.getTicketNo());
      if (ticket != null) {
        return JsonResult.createJsonResult(-1, "票号" + psg.getTicketNo() + "已存在于账单.");
      }
    }

    //校验服务费是否符合服务费模板设置
    JsonResult serviceChargeValidateResult = validateFlightOrderServiceCharge(enterpriseId, o);
    if (serviceChargeValidateResult != null) {
      return serviceChargeValidateResult;
    }


    int retCode = flightOrderDao.finishFlightOrder(enterpriseId, username, orderId, etdzDate);

    if (retCode > 0) {
      try {
        importService.importFlightOrder(enterpriseId, username, orderId);
      } catch (Exception ex) {
        logger.error(ex.getMessage());
        ex.printStackTrace();
      }
    }

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 校验机票订单的服务费
   */
  private JsonResult validateFlightOrderServiceCharge(final int enterpriseId, FlightOrder o) {
    //出票服务费
    final int ticketingFee = getTicketingServiceCharge(o);
    if (ticketingFee == -1) {
      return null;
    }

    if (ticketingFee > 0 && Math.abs(ticketingFee - o.getServiceCharge()) > 0.001) {
      final String tplCode = o.getCustomer().getTemplateCode().trim();
      CustomerServiceTemplate tpl = enterpriseDao.searchCustomerServiceTemplate(enterpriseId, tplCode);

      final String content = String.format("机票订单(%s): 服务费模板(%d)设定服务费为(%d), 实际(%f).",
              o.getOrderNo(), tpl.getStrength(), ticketingFee, o.getServiceCharge());
      doSendAlert(enterpriseId, content, null, EntDepartment.DEPT_ADMINISTRATOR);
      if (tpl.getStrength() == 10) {
        return JsonResult.createJsonResult(-2, content);
      }
    }

    return null;
  }


  /**
   * 修改开票属性
   */
  @Override
  public JsonResult updateFlightOrderTicketOptions(int enterpriseId, String username, int orderId,
                                                   int supplierId, int paymentMethodId, int autoTicket, int price) {
    Supplier supplier = enterpriseDao.getSupplierById(supplierId);
    if (supplier == null
            || supplier.getStatus() == 0
            || (supplier.getEnterpriseId() != 0 && supplier.getEnterpriseId() != enterpriseId)) {
      return JsonResult.createJsonResult(-1, "供应商不存在或已停用");
    }

    PaymentMethod pm = enterpriseDao.getPaymentMethodById(enterpriseId, paymentMethodId);
    if (pm == null || pm.getStatus() == 0) {
      return JsonResult.createJsonResult(-2, "付款方式不存在或已停用");
    }

    int retCode = flightOrderDao
            .updateTicketOptions(enterpriseId, username, orderId, supplier, pm, autoTicket, price);

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 修改机票订单的支付方式
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult updateFlightOrderPaymentMethod(int enterpriseId, String username, int orderId,
                                                   int paymentMethodId) {
    PaymentMethod o = enterpriseDao.getPaymentMethodById(paymentMethodId);
    if (o == null || o.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "支付方式不存在");
    }

    int retCode = flightOrderDao.updatePaymentMethod(enterpriseId, username, orderId, o);

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 修改机票订单的派送日期
   */
  @Override
  @Secured({RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_ADMIN})
  public JsonResult updateFlightOrderDeliveryDate(int enterpriseId, String username, int orderId,
                                                  Date deliveryDate) {
    // 校验派送日期不能小于今天
    if (deliveryDate.getTime() < DateUtil.getCurDate().getTime()) {
      return JsonResult.createJsonResult(-1, "派送日期不能早于今天");
    }
    int retCode = flightOrderDao.updateDeliveryDate(enterpriseId, username, orderId, deliveryDate);

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 修改出票员
   */
  @Override
  @Secured({RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_ADMIN})
  public JsonResult updateFlightOrderTicketer(int enterpriseId, String username, int orderId,
                                              String ticketer) {
    int retCode = flightOrderDao.updateTicketer(enterpriseId, username, orderId, ticketer);
    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 为订单回填票号
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_TICKETER})
  public JsonResult updateFlightOrderTicketNo(final int enterpriseId, final String username,
                                              final int orderId, List<FlightOrderPassenger> passengers) {

    FlightOrder orderInfo = flightOrderDao.getOrderDetailById(orderId);
    if (orderInfo == null || orderInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "无此订单");
    }

    // 校验供应商和支付方式是否已填写
    if (!isValidSupplier(enterpriseId, username, orderInfo)) {
      return JsonResult.createJsonResult(-2, "供应商和支付方式需要选择");
    }

    //TODO 校验出发日期，防止出错

    //判断是否需要模拟票号, 如果需要生成模拟票号，帮其生成
    fakeTicketNo(orderInfo, passengers);

    Set<String> ticketSet = new HashSet<>();
    for (FlightOrderPassenger psg : passengers) {
      final String ticketNo = psg.getTicketNo();

      List<String> ticketNos = FlightTicketUtil.processTicketNo(ticketNo);
      if (ticketNos.isEmpty()) {
        doSendAlert(enterpriseId, orderInfo.getOrderNo() + ", 回填的票号(" + ticketNo + ")不符合规范", null,
                EntDepartment.DEPT_TICKETING);
        return JsonResult.createJsonResult(-1, "票号(" + ticketNo + ")不符合规范");
      }

      psg.setTicketNo(ticketNos.get(0));
      psg.setTicketCount(ticketNos.size());
      //ticketNoEnd保存的是10位票号
      psg.setTicketNoEnd(ticketNos.get(ticketNos.size() - 1).substring(4));

      //将票号加入集合，校验是否有重复票号
      ticketSet.add(psg.getTicketNo());
    }

    if (ticketSet.size() < passengers.size()) {
      doSendAlert(enterpriseId, orderInfo.getOrderNo() + ", 请检查是否有重复票号", null,
              EntDepartment.DEPT_TICKETING);
      return JsonResult.createJsonResult(-3, "请检查是否有重复票号");
    }

    try {
      int retCode = flightOrderDao.updateTicketNo(enterpriseId, username, orderId, passengers);

      if (retCode > 0) {
        autoSendFlightOrderSms(enterpriseId, username, orderInfo);
      }

      return JsonResult.createJsonResult(retCode);
    } catch (RuntimeException ex) {
      ex.printStackTrace();
      return JsonResult.createJsonResult(-100, ex.getMessage());
    }
  }

  private boolean isValidSupplier(final int enterpriseId, final String username, final FlightOrder orderInfo) {
    if (orderInfo.getSupplier() != null && orderInfo.getPaymentMethod() != null) {
      return true;
    }
    final int orderId = orderInfo.getId();
    //当前是根管理员，则自动填写默认的供应商和支出方式，没有就自动创建一个默认的
    User user = userDao.getUserByUsername(username);
    if (user == null || user.getId() != enterpriseId) {
      //非超级管理员
      return false;
    }

    //当前用户是根管理员
    if (orderInfo.getSupplier() == null) {
      // 供应商
      Supplier supplier = enterpriseDao.getSupplierByDefault(enterpriseId);
      if (supplier == null) {
        return false;
      } else {
        int retCode = flightOrderDao.updateSupplier(enterpriseId, username, orderId, supplier, null);
        if (retCode != 1) {
          return false;
        }
      }
    }

    //支出支付方式
    if (orderInfo.getPaymentMethod() == null) {
      PaymentMethod pm = enterpriseDao.getPaymentMethodByDefault(enterpriseId);
      if (pm == null) {
        return false;
      } else {
        int retCode = flightOrderDao.updatePaymentMethod(enterpriseId, username, orderId, pm);
        return retCode == 1;
      }
    }

    return true;
  }

  /**
   * 检查是否需要为旅客生成模拟票号
   */
  private void fakeTicketNo(final FlightOrder orderInfo, List<FlightOrderPassenger> passengers) {
    final int enterpriseId = orderInfo.getEnterpriseId();
    final String twoCode = orderInfo.getFlights().get(0).getCarrierCode();

    for (FlightOrderPassenger psg : passengers) {
      String fakeTicketNo = orderNoGenerator.fakeTicketNo(enterpriseId, twoCode, 1);
      if (fakeTicketNo == null) {
        //返回null, 表示该航司未设置为模拟票号
        return;
      }

      psg.setTicketNo(fakeTicketNo);
    }
  }

  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult delFlightOrderExtra(int enterpriseId, String username, int orderId,
                                        int extraId) {
    int retCode = flightOrderDao.delFlightOrderExtra(enterpriseId, username, orderId, extraId);

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 同意 取消订单的请求
   *
   * @param enterpriseId 当前用户的enterpriseId
   * @param username     当前用户的username
   * @param orderId      机票订单的id
   * @param requestId    机票订单取消请求的id
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult agreeFlightOrderCancelRequest(int enterpriseId, String username, int orderId,
                                                  int requestId) {
    int retCode = flightOrderDao.agreeCancelRequest(enterpriseId, username, orderId, requestId);
    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 拒绝取消订单的请求
   *
   * @param enterpriseId 当前用户的enterpriseId
   * @param username     当前用户的username
   * @param orderId      机票订单的id
   * @param requestId    机票订单取消请求的id
   * @param reasonCode   拒绝的理由代码
   * @param reasonDesc   拒绝的理由描述
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult denyFlightOrderCancelRequest(int enterpriseId, String username, int orderId,
                                                 int requestId, int reasonCode, String reasonDesc) {
    int retCode = flightOrderDao
            .denyCancelRequest(enterpriseId, username, orderId, requestId, reasonCode, reasonDesc);
    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 行程单已邮寄
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_SUPERVISOR, "ROLE_DELIVERY"})
  public JsonResult finishFlightOrderItineraryDelivery(int enterpriseId, String username,
                                                       int orderId, String remark) {
    int retCode = flightOrderDao.finishItineraryDelivery(enterpriseId, username, orderId, remark);
    return JsonResult.createJsonResult(retCode);
  }


  @Override
  public JsonResult incFlightOrderPrintCount(int enterpriseId, String username, int orderId) {
    return JsonResult
            .createJsonResult(flightOrderDao.incFlightOrderPrintCount(enterpriseId, username, orderId));
  }

  /**
   * 给订单中的乘机人发送短信
   *
   * @param enterpriseId 企业id
   * @param username     用户名
   * @param orderId      订单id
   * @param psgId        乘机人id
   * @param mobileNo0    优先用此号码发送
   * @param smsContent0  自定义短信内容
   * @param smsFormat    短信内容使用格式
   */
  @Override
  public JsonResult sendFlightOrderSms(final int enterpriseId, final String username, final int orderId, final int psgId,
                                       final String mobileNo0, final String smsContent0, final int smsFormat) {
    final var expiredDate = enterpriseService.getEntExpireDate(enterpriseId);
    if (expiredDate != null && expiredDate.getTime() < DateUtil.getCurDate().getTime()) {
      return JsonResult.createJsonResult(-10000, "已过有效期");
    }

    return doSendFlightOrderSms(enterpriseId, username, orderId, psgId, mobileNo0, smsContent0, smsFormat);
  }


  /**
   * 获取订单乘机人的模板生成的短信内容
   */
  @Override
  public JsonResult getFlightOrderSmsContent(int enterpriseId, String username, int orderId,
                                             int psgId, int smsFormat) {

    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);

    String smsContent = generateFlightOrderSmsContentByPsgId(enterpriseId, flightOrder, psgId,
            smsFormat);
    if (smsContent != null) {
      return JsonResult.createJsonResult(1, smsContent);
    } else {
      return JsonResult.createJsonResult(-1, "无法生成短信内容");
    }
  }


  @Override
  public JsonResult sendFlightOrderApprovalSms(int enterpriseId, String username, int orderId,
                                               String mobileNo0, String smsContent0) {
    if (mobileNo0.length() > 0 && !CheckUtil.isMobileNo(mobileNo0)) {
      return JsonResult.createJsonResult(-1, "输入的手机号不符合格式要求");
    }

    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);

    String mobileNo = CheckUtil.isMobileNo(mobileNo0) ? mobileNo0 : flightOrder.getLinkPhone();
    if (!CheckUtil.isMobileNo(mobileNo)) {
      return JsonResult.createJsonResult(-1, "无符合要求的手机号");
    }

    final String smsContent = smsContent0 == null || smsContent0.trim().length() == 0
            ? generateFlightOrderApprovalSmsContent(enterpriseId, orderId) : smsContent0;

    SmsDetail smsDetail = SmsDetail.createSmsDetail(enterpriseId, username, mobileNo, smsContent, flightOrder.getOrderNo());

    return sendSms(smsDetail);
  }

  @Override
  public JsonResult getFlightOrderApprovalSmsContent(int enterpriseId, String username,
                                                     int orderId) {
    String smsContent = generateFlightOrderApprovalSmsContent(enterpriseId, orderId);
    return JsonResult.createJsonResult(1, smsContent);
  }


  /**
   * 修改机票订单的客户
   */
  @Override
  @Secured({RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_ADMIN})
  public JsonResult updateFlightOrderCustomer(int enterpriseId, String username, int orderId,
                                              int customerId, String costCenter) {
    if (customerId < 0) {
      return JsonResult.createJsonResult(-1, "数据错误");
    }

    if (customerId > 0) {
      Customer customer = customerDao.getCustomerById(customerId);
      if (customer == null || customer.getEnterpriseId() != enterpriseId) {
        return JsonResult.createJsonResult(-2, "不存在此客户");
      }
    }

    int retCode = flightOrderDao
            .updateFlightOrderCustomer(enterpriseId, username, orderId, customerId, costCenter);

    return JsonResult.createJsonResult(retCode);
  }

  @Override
  @Secured({RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_ADMIN})
  public JsonResult updateFlightOrderIntlTicket(int enterpriseId, String username, int orderId,
                                                int intlTicket0) {
    int intlTicket = intlTicket0 == 0 ? 0 : 1;
    int retCode = flightOrderDao
            .updateFlightOrderIntlTicket(enterpriseId, username, orderId, intlTicket);

    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public FlightOrderStatusStat statFlightMiscOrderStatus(int enterpriseId) {
    return flightOrderDao.statFlightMiscOrderStatus(enterpriseId);
  }

  @Override
  public ItineraryInfo createItineraryByFlightOrder(final int enterpriseId, final int orderId) {
    FlightOrder orderInfo = flightOrderDao.getOrderDetailById(orderId);

    ItineraryInfo info = new ItineraryInfo();

    info.setProjectName("Project Name");

    if (enterpriseId == 5) {
      info.setCompanySign("上海尚游");
    } else {
      info.setCompanySign("www.buk.cn");
    }

    for (FlightOrderFlight flt : orderInfo.getFlights()) {
      ItineraryFlightInfo flightInfo = new ItineraryFlightInfo();

      final String dport = flt.getFlight().getDepartureAirport();
      flightInfo.setDport(dport);
      flightInfo.setDportNameCn(flt.getFlight().getDepartureAirportName());
      flightInfo.setDportNameEn(getAirportNameEn(dport));

      final String aport = flt.getFlight().getArrivalAirport();
      flightInfo.setAport(aport);
      flightInfo.setAportNameCn(flt.getFlight().getArrivalAirportName());
      flightInfo.setAportNameEn(getAirportNameEn(aport));

      flightInfo.setFlightNo(flt.getFlight().getFlightNo());
      flightInfo.setSubclass(flt.getFlight().getSubclass());

      flightInfo.setDdate(flt.getFlight().getDepartureDate());

      flightInfo.setDtime(flt.getFlight().getDepartureTime());
      flightInfo.setAtime(flt.getFlight().getArrivalTime());

      flightInfo.setDterm(flt.getFlight().getDepartureTerminal());
      flightInfo.setAterm(flt.getFlight().getArrivalTerminal());
      info.addFlightInfo(flightInfo);
    }

    for (FlightOrderPassenger psg : orderInfo.getPassengers()) {
      ItineraryPassengerInfo passengerInfo = new ItineraryPassengerInfo();

      passengerInfo.setName(psg.getName());

      passengerInfo.setIdType(psg.getIdType());
      passengerInfo.setIdNo(psg.getIdNo());

      passengerInfo.setTicketNo(psg.getTicketNo());

      info.addPassengerInfo(passengerInfo);
    }

    return info;
  }

  @Override
  public void checkFlightOrderNeedApprover(final int ownerId, final String agentName) {
    List<FlightOrder> flightOrders = flightOrderDao.searchNeedAssignApproverFlightOrders(ownerId);
    for (FlightOrder o : flightOrders) {
      //根据订单提交的用户名来判断
      User user = userDao.getUserByUsername(o.getOperator());
      //判断是否为下级企业客户自助提交的订单
      if (user == null || user.getParentEnterprise() != ownerId) {
        continue;
      }

      final User mainUser = userDao.getUserById(user.getEnterpriseId());
      final int customerId = o.getCustomer().getId();

      String approver = doAssignOrderApprover(ownerId, user, mainUser, customerId);
      if (approver != null) {
        assignApproverFlightOrder(ownerId, agentName, o.getId(), approver);
        //给approver发送短信、邮件等审批提醒
        sendFlightOrderApproveAlert(ownerId, o.getOrderNo(), approver);
      }
    }
  }

  /**
   * 发送机票订单的审批提醒
   */
  private void sendFlightOrderApproveAlert(int ownerId, String orderNo, String approver) {
    LoginInfoDto loginInfoDto = getCustomerEmployeeLoginInfo(approver, null);
    String mobile = loginInfoDto.getMobile();
    String smsContent =
            "短信内容:机票订单(" + orderNo + ")需要审批，通知指定审批人(" + approver + ").";
    String email = loginInfoDto.getEmail();
    String emailSubject = "机票订单(" + orderNo + ")等待审批";
    String emailBody = "机票订单审批邮件发送内容";
    doSendAlertApprove(ownerId, mobile, smsContent, email, emailSubject, emailBody);
  }

  @Override
  public void checkFlightOrderMeetPolicy(final int enterpriseId, final String username) {
    List<FlightOrder> flightOrders = flightOrderDao.searchMeetPolicyFlightOrders(enterpriseId);

    for (FlightOrder o : flightOrders) {
      doCheckFlightOrderMeetPolicy(enterpriseId, username, o);
    }
  }

  private void doCheckFlightOrderMeetPolicy(final int enterpriseId, final String username, final FlightOrder o) {
    if (o.getCustomer() == null) {
      return;
    }

    final int customerId = o.getCustomer().getId();
    //1、获取乘机人的职位级别，如果有多个，则取最小的一个
    final int positionId = doGetFlightOrderPassengerPositionLevel(enterpriseId, customerId, o);
    if (positionId < 1) {
      //未找到该员工的职位级别，无法判断
      flightOrderDao.updateFlightOrderViolationStatus(enterpriseId, username, o.getId(), 0, "无相关员工职级资料");
      return;
    }

    //查找机票差旅政策
    List<CustomerTpFlight> tps = customerDao.searchCustomerTpFlights(enterpriseId, customerId);
    tps = tps.stream().filter(t -> t.getCustomerPosition().getPositionId() == positionId).collect(Collectors.toList());
    if (tps.isEmpty()) {
      flightOrderDao.updateFlightOrderViolationStatus(enterpriseId, username, o.getId(), 0, "无找到员工职级相匹配的差旅政策");
      return;
    }

    //默认违反，只要符合其中的一条就可以了
    int violationStatus = 1;
    String violationReason = "未找到符合的差旅政策";
    //准备航班的相关信息
    int cabinType = 0;
    int flyTime = 0;
    for (FlightOrderFlight flt : o.getFlights()) {
      final int newCabinType = getCabinType(flt.getCarrierCode(), flt.getFlight().getSubclass());
      cabinType = Math.max(cabinType, newCabinType);

      int newFlyTime = calcFlyTime(flt.getFlight().getDdate(), flt.getFlight().getArrivalDate(), flt.getFlight().getDtime(), flt.getFlight().getAtime());
      flyTime = Math.max(newFlyTime, flyTime);
    }

    for (CustomerTpFlight tpFlight : tps) {
      boolean isMatched = false;

      int cabinType0 = convertCabin(tpFlight.getCabinClassBefore());
      if (cabinType >= cabinType0) {
        //符合
        violationStatus = 0;
        violationReason = "符合：" + tpFlight;
        isMatched = true;
      }

      if (tpFlight.getFlyTime() > 0 && flyTime > tpFlight.getFlyTime()) {
        cabinType0 = convertCabin(tpFlight.getCabinClassAfter());
        if (cabinType >= cabinType0) {
          //符合
          violationStatus = 0;
          violationReason = "符合：" + tpFlight;
          isMatched = true;
        }
      }

      if (isMatched) {
        break;
      }
    }

    //TODO 应该说明符合或违法了那条政策
    flightOrderDao.updateFlightOrderViolationStatus(enterpriseId, username, o.getId(), violationStatus, violationReason);
  }


  /**
   * 获取机票订单中乘机人在所在公司的最小级别
   */
  private int doGetFlightOrderPassengerPositionLevel(final int enterpriseId, final int customerId, final FlightOrder o) {
    int positionLevel = 0;
    for (FlightOrderPassenger psg : o.getPassengers()) {
      int curPositionLevel = doGetPassengerPositionLevel(enterpriseId, customerId, psg.getName(), psg.getIdType(), psg.getIdNo());
      if (curPositionLevel > 0 && (positionLevel == 0 || positionLevel > curPositionLevel)) {
        positionLevel = curPositionLevel;
      }
    }

    return positionLevel;
  }

  /**
   * 自动取消“待提交”状态的客户自助下的订单
   *
   * @param enterpriseId 企业id, 必须是自然数（不包含0）
   */
  @Override
  public void checkFlightOrderStatus(final int enterpriseId, final String username) throws ParseException {
    if (enterpriseId < 1) {
      return;
    }

    List<FlightOrder> flightOrders = flightOrderDao.searchFlightOrderByStatus(enterpriseId, TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING, 100);

    for (FlightOrder flightOrder : flightOrders) {
      doCheckFlightOrderStatus(enterpriseId, username, flightOrder);
    }
  }

  private void doCheckFlightOrderStatus(final int enterpriseId, final String username, FlightOrder flightOrder) throws ParseException {
    final Date today = DateUtil.getCurDate();
    final String op1 = flightOrder.getOperator();
    User user = userDao.getUserByUsername(op1);
    if (user.getParentEnterprise() != enterpriseId) {
      //非企业客户的员工自助下单的，暂时不纳入自动取消操作
      return;
    }

    FlightOrder orderInfo = flightOrderDao.getOrderDetailById(flightOrder.getId());

    boolean canceled = false;
    //1、判断航班的起飞时间是否已过（这种情况应该只是测试期间会发生，因为订单没有人处理）
    for (FlightOrderFlight flightInfo : orderInfo.getFlights()) {
      Date ddate = DateUtil.convertToDate(flightInfo.getFlight().getDdate(), "yyyy-MM-dd");
      if (ddate.getTime() < today.getTime() && flightOrder.getCustomer().getTestMode() == 0) {
        canceled = true;
        break;
      }
    }

    if (canceled) {
      flightOrderDao.cancelFlightOrder(enterpriseId, username, orderInfo.getId(), 100, "航班起飞日期已经过了");
      return;
    }

    //2.判断付款方式，如果已付款，则不管时间, 等待客户提交
    if (orderInfo.getPayStatus() == 1) {
      return;
    }

    //3.判断下单时间是否超过1小时
    final long pastHours = DateUtil.getPastHours(orderInfo.getCreateTime());
    if (pastHours >= 1) {
      doCancelFlightOrder(enterpriseId, username, flightOrder, "超时1小时仍未提交，自动取消。");

      return;
    }

    //4.验价时间为基准，超过10分钟还是暂不状态，自动取消
    if (flightOrder.getPriceChecked() == ORDER_PRICE_VERIFY_STATE_DONE) {
      final Date priceCheckedTime = flightOrder.getPriceCheckedTime();
      if (priceCheckedTime == null) {
        return;
      }

      final int elapsed = DateUtil.getPastMinutes(priceCheckedTime);
      if (elapsed >= 10) {
        //取消订单
        doCancelFlightOrder(enterpriseId, username, flightOrder, "已验价，超时10分钟未提交，取消。");
      }
    }
  }

  private void doCancelFlightOrder(final int enterpriseId, final String username, FlightOrder flightOrder, String remark) {
    //1.取消编码
    if (flightOrder.getPnrNo() != null && !flightOrder.getPnrNo().contains("xxx")) {
      int retCode = etermService.cancelFlightOrderPnr(enterpriseId, username, flightOrder, remark);
      if (retCode != 1) {
        //第一次取消失败，再尝试一次
        etermService.cancelFlightOrderPnr(enterpriseId, username, flightOrder, remark);
      }
    }
    //2.取消订单
    flightOrderDao.cancelFlightOrder(enterpriseId, username, flightOrder.getId(), 100, remark);
  }

  @Override
  public void refreshFlightOrderPnrDetail(final int enterpriseId, final String username) {
    List<FlightOrder> flightOrders = flightOrderDao.searchFlightOrdersNeedRefreshPnr(enterpriseId, 100);
    for (FlightOrder flightOrder : flightOrders) {
      doRefreshFlightOrderPnrDetail(enterpriseId, username, flightOrder);
    }
  }

  private void doRefreshFlightOrderPnrDetail(final int enterpriseId, final String username, FlightOrder flightOrder) {
    if (flightOrder.getPnrRefreshStatus() != -1) {
      return;
    }

    if (flightOrder.getPnrNo() == null || flightOrder.getPnrNo().trim().length() < 6) {
      return;
    }

    if (flightOrder.getStatus() != TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED) {
      return;
    }

    doRefreshFlightOrderPnrDetail(enterpriseId, username, flightOrder.getId(), flightOrder.getOrderNo(), flightOrder.getPnrNo());
  }

  @Override
  public void refreshFlightOrderDetrDetail(final int enterpriseId, final String username) {
    //查找需要更新DETR的机票订单, 每次10个订单，以防占用太多ETERM资源
    List<FlightOrder> flightOrders = flightOrderDao.searchFlightOrdersNeedRefreshDetr(enterpriseId, 25);
    if (!flightOrders.isEmpty()) {
      logger.info("refreshFlightOrderDetrDetail's size: {}.", flightOrders.size());
    }
    for (var o : flightOrders) {
      if (o.getDetrRefreshTime() != null && DateUtil.getPastMinutes(o.getDetrRefreshTime()) < 24 * 60) {
        //DETR更新24小时以内的，不再次更新
        logger.warn("{}, {}: {}, {}", o.getOrderNo(), o.getId(), o.getDetrRefreshStatus(), o.getDetrRefreshTime());
        flightOrderDao.updateFlightOrderDetrStatus2(enterpriseId, username, o.getId(), 1, "< 24 hours.");
        continue;
      }

      if (StringUtils.trimToEmpty(o.getPnrNo()).length() < 6) {
        flightOrderDao.updateFlightOrderDetrStatus2(enterpriseId, username, o.getId(), 4, "Pnr is empty.");
        continue;
      }

      if (o.getSupplier() != null &&
              o.getSupplier().getIsBsp() != FLIGHT_AUTO_TICKETING_BSP &&
                      o.getSupplier().getIsBsp() != FLIGHT_AUTO_TICKETING_NDC_CZ) {
        flightOrderDao.updateFlightOrderDetrStatus2(enterpriseId, username, o.getId(), 4, "Supplier is not bsp or cz_ndc.");
        continue;
      }

      execRefreshFlightOrderDetr(enterpriseId, username, o);
    }

    //将DETR结果反馈到机票订单
    flightOrders = flightOrderDao.searchFlightOrdersDetrFinished(enterpriseId, 100);
    for (FlightOrder flightOrder : flightOrders) {
      mergeDetrResult2FlightOrder(enterpriseId, username, flightOrder.getId());
    }
  }

  /**
   * 检查机票订单是否符合需要刷新DETR的条件，符合条件的则进一步去更新
   *
   * @param enterpriseId 企业id
   * @param username     用户名
   * @param o  订单信息
   */
  private void execRefreshFlightOrderDetr(final int enterpriseId, final String username, FlightOrder o) {
    if (o.getDetrRefreshStatus() != 0
            || StringUtils.trimToEmpty(o.getPnrNo()).length() < 6
            || o.getStatus() != TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED) {
      logger.warn("{}, {}: {}, {}, {}, {}", o.getOrderNo(), o.getId(), o.getDetrRefreshStatus(),
              o.getDetrRefreshTime(), o.getPnrNo(), o.getStatus());
      return;
    }

    etermService.refreshFlightOrderDetrDetail(enterpriseId, username, flightOrderDao.getOrderDetailById(o.getId()));
  }

  /**
   * 将DETR的结果合并到机票订单
   */
  private void mergeDetrResult2FlightOrder(final int enterpriseId, final String username, final int orderId) {
    //获取订单包含的详情
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);

    if (flightOrder.getDetrRefreshStatus() != 1) {
      return;
    }

    List<FlightOrderTicket> tickets = getFlightOrderTickets(flightOrder);
    if (tickets.isEmpty()) {
      return;
    }

    for (FlightOrderTicket ticket : tickets) {
      refreshFlightOrderTicketDetrInfo(ticket, flightOrder.getEnterpriseId());
    }

    //过滤掉不符合要求的
    tickets = tickets.stream().filter(t -> t.getTicketStatus() != -100).collect(Collectors.toList());

    flightOrderDao.updateFlightOrderTicketStatus(enterpriseId, username, flightOrder.getId(), tickets);
  }

  private void refreshFlightOrderTicketDetrInfo(FlightOrderTicket ticket, final int enterpriseId) {
//从DETR中获取对应的航段信息
    DetrInfo detrInfo = detrDao.getByTicketNo(enterpriseId, ticket.getTicketNo().replace("-", ""));
    if (detrInfo == null) {
      return;
    }

    if (detrInfo.getDetrDetail().contains("ET TICKET NUMBER IS NOT EXIST")) {
      ticket.setTicketStatus(128);
      return;
    }

    for (DetrInfoFlight detrInfoFlight : detrInfo.getFlights()) {
      if (ticket.getDport().equalsIgnoreCase(detrInfoFlight.getDport())
              && ticket.getAport().equalsIgnoreCase(detrInfoFlight.getAport())
      ) {
        final String tktStatus = detrInfoFlight.getTicketStatus().trim();
        ticket.setSegUsedStatus(tktStatus);
        if (DETR_OPEN_FOR_USE.equalsIgnoreCase(tktStatus)) {
          ticket.setTicketStatus(0);
        } else if (DETR_USED_FLOWN.equalsIgnoreCase(tktStatus)) {
          ticket.setTicketStatus(1);
        } else if (DETR_EXCHANGED.equalsIgnoreCase(tktStatus)) {
          ticket.setTicketStatus(2);
        } else if (DETR_REFUNDED.equalsIgnoreCase(tktStatus)) {
          ticket.setTicketStatus(3);
        } else {
          logger.info(tktStatus);
        }
      }
    }
  }

  private List<FlightOrderTicket> getFlightOrderTickets(final FlightOrder flightOrder) {
    final int enterpriseId = flightOrder.getEnterpriseId();

    List<FlightOrderTicket> tickets = flightOrderDao.searchFlightOrderTickets(enterpriseId, flightOrder.getId());

    for (FlightOrderPassenger psg : flightOrder.getPassengers()) {
      List<String> ticketNos = FlightTicketUtil.processTicketNo(psg.getTicketNo(), psg.getTicketCount());
      for (final String ticketNo : ticketNos) {
        processPassengerTickets(tickets, flightOrder, ticketNo, psg.getId());
      }
    }

    return tickets;
  }

  private void processPassengerTickets(List<FlightOrderTicket> tickets, final FlightOrder flightOrder,
                                       final String ticketNo, final int psgId) {
    final int enterpriseId = flightOrder.getEnterpriseId();

    for (FlightOrderFlight flt : flightOrder.getFlights()) {
      boolean existed = false;
      for (FlightOrderTicket ticket : tickets) {
        if (ticket.getTicketNo().equalsIgnoreCase(ticketNo)
                && ticket.getFlightId() == flt.getId()
                && ticket.getPassengerId() == psgId) {
          existed = true;
          break;
        }
      }

      if (!existed) {
        FlightOrderTicket ticket = new FlightOrderTicket();
        tickets.add(ticket);

        ticket.setEnterpriseId(enterpriseId);
        ticket.setOrder(flightOrder);

        ticket.setPassengerId(psgId);
        ticket.setFlightId(flt.getId());
        ticket.setTicketNo(ticketNo);

        ticket.setDport(flt.getFlight().getDport());
        ticket.setAport(flt.getFlight().getAport());
        ticket.setFlightNo(flt.getFlight().getFlightNo());
        ticket.setDdate(flt.getFlight().getDdate());
        ticket.setSubclass(flt.getFlight().getSubclass());
        ticket.setDtime(flt.getFlight().getDtime());
        ticket.setTicketStatus(-100);
      }
    }
  }

  @Override
  public void recheckFlightOrderDetrInfo(final int enterpriseId, final String username) {
    List<FlightOrder> flightOrders = flightOrderDao.searchFlightOrdersNeedRecheckDetr(enterpriseId, 100);

    for (FlightOrder o : flightOrders) {
      if (o.getDetrRefreshTime() == null) {
        continue;
      }

      if (isNeedToResetDetrStatus(enterpriseId, username, o.getId())) {
        //重置DETR状态，更新DETR信息
        flightOrderDao.updateFlightOrderDetrStatus2(enterpriseId, username, o.getId(), 0, "reset");
      }
    }
  }

  /**
   * 检查机票订单是否需要重置DETR更新状态为“待更新”
   */
  private boolean isNeedToResetDetrStatus(final int enterpriseId, final String username, final int orderId) {
    //检查票号是否都到了终态
    FlightOrderResponseDto detail = this.getFlightOrderDetail(enterpriseId, 0, orderId);
    if (detail.getErrcode() != 0) {
      return false;
    }

    int retCode = checkFlightOrderTicketIsFinalStatus(enterpriseId, username, detail);
    if (retCode == 1) {
      //已是终态
      return false;
    }

    //考虑下航班日期还有多少天
    final Date minDdate = FlightOrderUtil.getMinDepartureDate(detail);
    if (minDdate == null) {
      return false;
    }

    final int spanDays = -DateUtil.getPastDays(minDdate);
    final long pastHours = DateUtil.getPastHours(detail.getDetrRefreshTime());
    if (spanDays >= 4 && pastHours <= 72) {
      return false;
    }

    return pastHours >= 24;
  }

  @Override
  public JsonResult createFlightOrderByPnrDetail(int enterpriseId, String username, String pnrDetail) {
    PnrDto pnrInfo = new PnrDto();
    pnrInfo.setPnrDetail(pnrDetail);

    FlightOrderRequestDto dto = new FlightOrderRequestDto();

    dto.setPayType(PAY_TYPE_BILL);
    dto.setPnrNo(pnrInfo.getPnrNo());
    dto.setKeyCustomerNo(pnrInfo.getKeyCustomerNo());
    dto.setItineraryType(0);
    dto.setOp1(username);
    dto.setIntlTicket(pnrInfo.getType3());
    dto.setPnrDetail(pnrDetail);

    dto.getAdultPrice().setPrice(pnrInfo.getPrice());
    dto.getAdultPrice().setParValue(pnrInfo.getParValue());
    dto.getAdultPrice().setTax(pnrInfo.getTax());
    dto.setAdultCount(pnrInfo.getAdultCount());

    for (PnrFlightDto fltDto : pnrInfo.getFlights()) {
      PnrFlightDto flt = new PnrFlightDto();
      BeanUtils.copyProperties(fltDto.getFlight(), flt.getFlight());

      dto.getFlights().add(flt);

    }

    for (PnrPassengerDto psgDto : pnrInfo.getPassengers()) {
      PnrPassengerDto psg = new PnrPassengerDto(0, "");

      psg.setPsgName(psgDto.getName());
      psg.setMobile(psgDto.getMobile());
      psg.setIdNo(psgDto.getIdNo());
      psg.setIdType(psgDto.getIdType());
      psg.setTicketNo(psgDto.getTicketNo());
      psg.setSelected(true);

      dto.getPassengers().add(psg);
    }

    return createFlightOrder(enterpriseId, username, dto);
  }

  @Override
  public void validateFlightBusinessRule(final int enterpriseId, final String username) {
    List<FlightOrder> orders = flightOrderDao.searchFlightOrdersNeedValidate(enterpriseId);
    for (FlightOrder orderInfo : orders) {
      doValidateFlightBusinessRule(orderInfo);
    }
  }

  private void doValidateFlightBusinessRule(final FlightOrder orderInfo) {
    if (orderInfo.getCustomer() == null || orderInfo.getCustomer().getTemplateCode() == null) {
      return;
    }

    final int enterpriseId = orderInfo.getEnterpriseId();

    CustomerServiceTemplate tpl = enterpriseDao.searchCustomerServiceTemplate(enterpriseId, orderInfo.getCustomer().getTemplateCode());
    if (tpl == null || tpl.getFlightInsurance() <= 0) {
      return;
    }
    //校验是否有对应的保险单
    //获取相关的 服务
    int count = 0;
    List<VasOrder> vasOrders = vasOrderDao.searchByFlightOrderId(enterpriseId, orderInfo.getId());
    for (VasOrder vasOrder : vasOrders) {
      if (vasOrder.getProductType() == TieInProduct.INSURANCE && vasOrder.getStatus() != ORDER_STATUS_CANCELED) {
        count++;
      }
    }
    if (count == 0) {
      doSendAlert(enterpriseId, String.format("测试：订单%s缺少保险单", orderInfo.getOrderNo()), "Dai", 0);
    }
  }

  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult confirmFlightOrderNotice(final int enterpriseId, final String username, final int orderId) {
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-404, "无此订单");
    }

    int retCode = flightOrderDao.confirmFlightOrderNotice(enterpriseId, username, orderId);
    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public void checkFlightOrderShowStatus(final int enterpriseId, final String username) {
    List<FlightOrder> orders = flightOrderDao.searchFlightOrdersNeedRecheckShowStatus(enterpriseId, 1000);
    for (FlightOrder orderInfo : orders) {
      doCheckFlightOrderShowStatus(enterpriseId, username, orderInfo);
    }
  }

  private void doCheckFlightOrderShowStatus(final int enterpriseId, final String username, final FlightOrder orderInfo) {
    final int orderId = orderInfo.getId();
    FlightOrderResponseDto dto = getFlightOrderDetail(enterpriseId, 0, orderId);
    //看是否全部使用
    int usedCount = 0;
    int refundCount = 0;
    int changeCount = 0;
    for (BaseFlightOrderPassengerDto psg : dto.getPassengers()) {
      if (psg.getTicketStatus() == FLIGHT_TICKET_STATUS_USED) {
        usedCount++;
      } else if (psg.getTicketStatus() == FLIGHT_TICKET_STATUS_REFUNDED) {
        refundCount++;
      } else if (psg.getTicketStatus() == FLIGHT_TICKET_STATUS_EXCHANGED) {
        changeCount++;
      }
    }

    final int psgCount = dto.getPassengers().size();
    if (usedCount == psgCount) {
      //已全部使用
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_WHOLE_USED);
    } else if (refundCount == psgCount) {
      //已全部退票
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_WHOLE_REFUNDED);
    } else if (changeCount == psgCount) {
      //已全部改签
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_WHOLE_EXCHANGED);
    } else if ((usedCount + changeCount) == psgCount) {
      //部分使用，部分改签
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_PARTIAL_USED
              + FLIGHT_ORDER_TICKET_PARTIAL_EXCHANGED);
    } else if ((usedCount + refundCount) == psgCount) {
      //部分使用，部分退票
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_PARTIAL_USED
              + FLIGHT_ORDER_TICKET_PARTIAL_REFUNDED);
    } else if ((changeCount + refundCount) == psgCount) {
      //部分改签，部分退票
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_PARTIAL_EXCHANGED
              + FLIGHT_ORDER_TICKET_PARTIAL_REFUNDED);
    } else if ((usedCount + changeCount + refundCount) == psgCount) {
      //部分使用， 部分改签，部分退票
      flightOrderDao.updateFlightOrderShowStatus(enterpriseId, username, orderId, FLIGHT_ORDER_TICKET_PARTIAL_USED
              + FLIGHT_ORDER_TICKET_PARTIAL_EXCHANGED
              + FLIGHT_ORDER_TICKET_PARTIAL_REFUNDED);
    }
  }

  @Override
  public JsonResult saveFlightOrderAttachment(int enterpriseId, String username, int orderId, final int orderType,
                                              List<OrderAttachment> attachments) {
    for (OrderAttachment oa : attachments) {
      oa.setEnterpriseId(enterpriseId);
      oa.setOrderType(orderType);
      oa.setOrderId(orderId);
    }

    int retCode = flightOrderDao.saveFlightOrderAttachment(enterpriseId, username, orderId, orderType, attachments);

    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public void checkFlightOrderNotifyStatus(int enterpriseId, String username) {
    var flightOrders = flightOrderDao.searchFlightOrderByStatus(enterpriseId, TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING, 100);

    for (FlightOrder orderInfo : flightOrders) {
      doCheckFlightOrderNotifyStatus(enterpriseId, username, orderInfo);
    }
  }

  private void doCheckFlightOrderNotifyStatus(final int enterpriseId, final String username, final FlightOrder orderInfo) {
    final Date today = DateUtil.getCurDate();
    if (orderInfo.getCreateTime().getTime() < today.getTime()) {
      //创建时间为前一天的，不发送提醒通知
      return;
    }

    if (orderInfo.getNotifyStatus() == 1) {
      //已经通知了
      return;
    }

    String op1 = orderInfo.getOperator();
    User user = userDao.getUserByUsername(op1);
    if (user.getParentEnterprise() != enterpriseId) {
      //非企业客户的员工自助下单的，不处理
      return;
    }

    //测试模式的客户，不发送提醒信息
    if (orderInfo.getCustomer() != null && orderInfo.getCustomer().getTestMode() == 1) {
      return;
    }

    //发送通知提醒业务员
    try {
      String content = String.format("有新的机票订单了, 订单号：%s, 客户：%s.", orderInfo.getOrderNo(), user.getUsername());
      flightOrderDao.updateFlightOrderNotifyStatus(enterpriseId, username, orderInfo.getId(), content);

      // 校验订单中的乘机人和航班信息是否重复
      threadPool.execute(() -> validateFlightOrderDuplicate(orderInfo));

      sendFlightOrderAlertToWwAppChat(enterpriseId, content);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  @Override
  public JsonResult uploadPassenger4FlightOrder(final int enterpriseId, final String username, final int orderId,
                                                final String fileName) {
    List<PassengerDto> list = new ArrayList<>();

    try {
      File target = new File(fileName);
      FileInputStream fi = new FileInputStream(target);
      Workbook wb = create(fi);

      generatePassengersFromExcel(list, wb);
    } catch (Exception ex) {
      ex.printStackTrace();
      logger.error(ex.getMessage());
      return JsonResult.createJsonResult(-1, ex.getMessage());
    }

    if (list.isEmpty()) {
      return JsonResult.createJsonResult(-1, "没有数据");
    }

    var flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (!flightOrder.getPassengers().isEmpty()) {
      return JsonResult.createJsonResult(-2, "订单中已有乘机人");
    }

    for (PassengerDto dto : list) {
      //校验
      if (StringUtils.isEmpty(dto.getName())) {
        return JsonResult.createJsonResult(-5, "姓名(" + dto.getName() + ")" + "不符合格式要求");
      }

      if (dto.getIdType() <= 0) {
        return JsonResult.createJsonResult(-5, "请选择证件类型(" + dto.getName() + ")");
      }

        if (dto.getIdNo().length() < 2) {
          return JsonResult.createJsonResult(-5, "证件号(" + dto.getIdNo() + ")" + "不符合格式要求");
        }
        if (dto.getIdType() == ID_TYPE_NI && dto.getIdNo().trim().length() != 18) {
          //身份证的校验应该更准确
          return JsonResult.createJsonResult(-5, "身份证(" + dto.getIdNo() + ")" + "不符合格式要求");
        }


      FlightOrderPassenger psg = new FlightOrderPassenger();

      psg.setName(dto.getName());
      psg.setIdNo(dto.getIdNo());
      psg.setIdType(dto.getIdType());
      psg.setMobile(dto.getMobile());
      psg.setPsgType(dto.getPsgType());

      flightOrder.getPassengers().add(psg);
    }

    try {
      int retCode = flightOrderDao.savePassengers(enterpriseId, username, orderId, flightOrder.getPassengers());

      return JsonResult.createJsonResult(retCode);
    } catch (Exception ex) {
      return JsonResult.createJsonResult(-100, ex.getMessage());
    }
  }

  @Override
  public FlightOfferPrice checkFlightOrderPrice(final int enterpriseId, final String username, final int orderId) {
    var flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      var result = new FlightOfferPrice();
      result.setErrcode(404);
      result.setErrmsg("未找到订单");
      return result;
    }

    var fop = offerCzNdcPrice(enterpriseId, username, flightOrder);

    if (fop.getErrcode() == 0) {
      //保存到redis
//      final String key = "ndc_cz_offer_price_" + fop.getShoppingResponseId();
      final String key = String.format(CZ_NDC_SHOPPING_RESPONSE, enterpriseId, fop.getShoppingResponseId());
      final String value = JSON.toJSONString(fop);

      redisDao.setAndExpire(key, value.getBytes(UTF_8), 60L * 10);
    } else {
      //标识该订单获取CZ NDC验价时出错，不再用NDC开票
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, fop.getErrmsg());

      final String key = String.format(CZ_NDC_ORDER_KEY, enterpriseId, flightOrder.getOrderNo());
      final String value = JSON.toJSONString(fop.getErrmsg());

      redisDao.setAndExpire(key, value.getBytes(UTF_8), 60L * 10);
    }

    return fop;
  }


  @Override
  public JsonResult confirmFlightOrderPrice(int enterpriseId, String username, int orderId, String shoppingResponseId,
                                            List<String> itemCodes) {
    var flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "订单不存在");
    }

    if (!StringUtils.isBlank(flightOrder.getExternalOrderNo())) {
      return JsonResult.createJsonResult(-2, "已有外部订单号");
    }

    //校验本地的价格
    final String key = String.format(CZ_NDC_SHOPPING_RESPONSE, enterpriseId, shoppingResponseId);
    final String jsonStr = redisDao.getStr(key, UTF_8);
    logger.info(jsonStr);
    var rs0 = JSON.parseObject(jsonStr, FlightOfferPrice.class);

    //成人、儿童价格
    List<String> offerItemIds = new ArrayList<>(itemCodes);

    //修改订单的价格
    //获取到了佣金，计算机票订单的成本
    FlightOrderRequestDto dto = FlightOrderUtil.createDtoByFlightOrder(flightOrder);

    var ost = enterpriseService.searchServiceTemplateByCustomer(enterpriseId, flightOrder.getCustomerId());
    var serviceTemplate = ost.orElse(null);

    if (dto.getAdultCount() > 0) {
      adjustFlightOrderPrice(dto, PSG_TYPE_ADU, offerItemIds, rs0, serviceTemplate);
    }
    if (dto.getChildCount() > 0) {
      adjustFlightOrderPrice(dto, PSG_TYPE_CHD, offerItemIds, rs0, serviceTemplate);
    }

    flightOrderDao.updatePrice(enterpriseId, username, orderId, dto);

    var rq = new OrderCreateRequest();

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_MOBILE));

    for (var psg : flightOrder.getPassengers()) {
      cn.buk.api.airskyhk.dto.ContactInfo contactInfo = new cn.buk.api.airskyhk.dto.ContactInfo();

      contactInfo.setContactName(getFlightOrderLinkMan(flightOrder));
      contactInfo.setMobile(getFlightOrderLinkMobile(flightOrder));
      contactInfo.setPhoneNumber(getFlightOrderLinkPhone(flightOrder));
      contactInfo.setEmail(getFlightOrderLinkEmail(flightOrder));

      contactInfo.setSurname(psg.getName());
      rq.getRequest().getContacts().add(contactInfo);
    }

    for (var psg : flightOrder.getPassengers()) {
      preparePsgRequest4CzNdcOrder(psg, rq);
    }

    rq.getRequest().getOfferItemIds().addAll(offerItemIds);
    //优惠券(乘机人、offerItemId、优惠券)
    for(var offerItemId: offerItemIds) {
      prepareCouponInfo4CzNdcOrder(rq, offerItemId, rs0);
    }



    rq.getRequest().setTotalAmount(dto.getTotalCost());
    rq.getRequest().setTotalTax(dto.getAdultPrice().getTax());

    rq.getRequest().setShoppingResponseId(shoppingResponseId);



    //大客户代码
    if (StringUtils.isNotEmpty(flightOrder.getKeyCustomerNo())) {
      final String rawKeyCustomerNo = queryRawKeyCustomerNo(enterpriseId, flightOrder.getKeyCustomerNo());
      rq.getRequest().setBigClientId(rawKeyCustomerNo);
      rq.getPayloadAttributes().setSystem("B2G");
    }

    //记录查询日志
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "CZ_NDC_创建订单", flightOrder.getOrderNo());
    rq.getPayloadAttributes().setTransactionId(queryId + "");

    var ndcService = createNdcService(enterpriseId);
    if (ndcService == null) {
      return JsonResult.createJsonResult(-1, "请联系管理员，配置CZ_NDC相关参数");
    }

    var rs = ndcService.createOrder(rq);
    userDao.updateEtermCmdLog(queryId, rs.getRequestStr(), rs.getResponseStr());


    if (SUCCESS.equalsIgnoreCase(rs.getStatus().getStatus())) {
      String externalOrderNo = rs.getResponse().getOrder().getOrderNo();
      String pnr = rs.getResponse().getOrder().getPnr();
      String remark = "保存外部订单号(" + externalOrderNo + ")和大编码(" + pnr + ")";
      flightOrderDao.updateExternalOrderNo(enterpriseId, username, orderId, externalOrderNo, pnr, remark);

      return JsonResult.createJsonResult(1, "外部订单号：" + externalOrderNo + "; 大编码: " + pnr);
    } else {
      final String msg = rs.getStatus().getErrorCode() + ": " + rs.getStatus().getErrorMessage();
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, msg);

      return JsonResult.createJsonResult(-1, rs.getStatus().getErrorMessage());
    }
  }

  private void prepareCouponInfo4CzNdcOrder(OrderCreateRequest rq, final String offerItemId, FlightOfferPrice rs0) {
    String segmentKey = null;
    //通过offerItemId找到哪一个航段
    for(var seg: rs0.getSegments()) {
      for(var fare: seg.getFareInfoDtos()) {
        if (fare.getOfferItemId().equalsIgnoreCase(offerItemId)) {
          segmentKey = seg.getSegmentKey();
          logger.info("{}, {}", segmentKey, offerItemId);
          break;
        }
      }
      if (segmentKey != null) {
        break;
      }
    }

    if (segmentKey == null) {
      return;
    }

    String couponNo = null;
    int faceValue = 0;
    for(var seg: rs0.getSegments()) {
      if (!seg.getSegmentKey().equalsIgnoreCase(segmentKey)) {
        logger.info(seg.getSegmentKey());
        continue;
      }
      for(var coupon: seg.getCouponInfos()) {
        couponNo = coupon.getCouponNo();
        faceValue = coupon.getFaceValue();
        break;
      }
      if (couponNo != null) {
        break;
      }
    }
    if (couponNo == null) {
      return;
    }

    //匹配了
    for(var psg: rq.getRequest().getPassengers()) {
      var couponInfo = new BaseOrderCoupon();
      couponInfo.setFaceValue(faceValue);
      couponInfo.setOfferItemId(offerItemId);
      couponInfo.setCouponNo(couponNo);
      couponInfo.setSurname(psg.getSurname());
      logger.info("{}, {}, {}", offerItemId, couponNo, faceValue);
      rq.getRequest().getCoupons().add(couponInfo);
    }
  }

  private void preparePsgRequest4CzNdcOrder(FlightOrderPassenger psg, OrderCreateRequest rq) {
    PassengerInfo psgInfo = new PassengerInfo();
    psgInfo.setSurname(psg.getName());
    if (psg.getIdType() == ID_TYPE_NI) {
      psgInfo.setIdentityType("NI");
    } else if (psg.getIdType() == ID_TYPE_PASSPORT) {
      psgInfo.setIdentityType("PP");
    } else {
      psgInfo.setIdentityType("ID");
    }
    psgInfo.setIdentityNumber(psg.getIdNo());

    //CZ NDC中乘客生日必须填
    if (psg.getBirthday() == null && psg.getIdType() == ID_TYPE_NI) {
      psg.setBirthday(IdUtil.retrieveBirthday(psg.getIdNo().trim()));
    }

    if (psg.getBirthday() != null) {
      psgInfo.setBirthdate(DateUtil.formatDate(psg.getBirthday(), DATE_YYYY_MM_DD));
    }

    if (psg.getGender() == null && psg.getIdType() == ID_TYPE_NI) {
      psg.setGender(IdUtil.retrieveGender(psg.getIdNo().trim()));
    }

    if (psg.getGender() != null && psg.getGender() == Constant.GENDER_MALE) {
      psgInfo.setGender("男");
    } else if (psg.getGender() != null && psg.getGender() == Constant.GENDER_FEMALE) {
      psgInfo.setGender("女");
    }

    psgInfo.setPassengerType("ADT");
    rq.getRequest().getPassengers().add(psgInfo);
  }

  private String getFlightOrderLinkEmail(FlightOrder flightOrder) {
    var val = flightOrder.getLinkEmail();
    if (StringUtils.isEmpty(val)) {
      val = doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_EMAIL);
    }
    return val;
  }

  private String getFlightOrderLinkMobile(FlightOrder flightOrder) {
    var val = flightOrder.getLinkMobile();
    if (StringUtils.isEmpty(val)) {
      val = doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_MOBILE);
    }
    return val;
  }

  private String getFlightOrderLinkPhone(FlightOrder flightOrder) {
    var val = flightOrder.getLinkPhone();
    if (StringUtils.isEmpty(val)) {
      val = doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_PHONE);
    }
    return val;
  }

  /**
   * 获取订单联系人或默认联系人
   */
  private String getFlightOrderLinkMan(FlightOrder flightOrder) {
      return doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINKMAN);
  }

  @Override
  public JsonResult syncFlightOrderStatus(int enterpriseId, String username, int orderId, final int actMode) throws BaseException {
    var flightOrder = flightOrderDao.getOrderDetailById(orderId);

    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "Order is not existed.");
    }

    if (flightOrder.getSupplier() == null) {
      throw new BaseException(10000, "Supplier is not set.");
    }

    if (StringUtils.isBlank(flightOrder.getExternalOrderNo())) {
      return JsonResult.createJsonResult(-3, "无外部订单号");
    }

    if (flightOrder.getSupplier().getEnterpriseId() == 0) {
      //平台供应商
      return syncEotmsFlightOrderStatus(enterpriseId, username, orderId);
    }

    if (flightOrder.getSupplier().getIsBsp() != FLIGHT_AUTO_TICKETING_NDC_CZ) {
      return JsonResult.createJsonResult(-2, "not CZ NDC supplier.");
    }



    var rs = execSearchNdcOrder(enterpriseId, username, flightOrder.getOrderNo(),
            flightOrder.getExternalOrderNo(), flightOrder.getBigPnrNo());

    return processCzNdcOrderSyncResult(enterpriseId, username, flightOrder, rs, actMode);
  }

  @Override
  public JsonResult syncExternalOrderStatus(int enterpriseId, String username, int externalOrderId) throws BaseException {
    var externalOrder = flightOrderDao.getExternalOrder(externalOrderId);
    if (externalOrder == null) {
      return JsonResult.createJsonResult(-1, "此外部订单不存在");
    }

    var rs = doSyncEotmsFlightOrderStatus(enterpriseId, username, externalOrder.getSupplierId(), externalOrder.getExternalOrderNo());
    if (rs.getErrcode() != 0) {
      return JsonResult.createJsonResult(-1, rs.getErrmsg());
    }

    if (rs.getOrderStatus() == FLIGHT_ORDER_STATUS_CANCELED) {
      //删除该外部订单
      flightOrderDao.removeExternalOrder(enterpriseId, username, externalOrderId, null);
      return JsonResult.createJsonResult(-1, "外部订单已取消");
    } else if (rs.getPriceCheckedStatus() != 1) {
      return JsonResult.createJsonResult(-1, "卖家价格未确认");
    } else {
      //卖家确认价格，本地校验价格
      double price = 0;
      double parValue = 0;
      double tax = 0;
      double commission = 0;
      double serviceCharge = 0;
      double discount = 0;
      for (var priceDto : rs.getPrices()) {
        if (priceDto.getPriceType() == PRICE_TYPE_ADULT) {
          price = priceDto.getPrice();
          parValue = priceDto.getParValue();
          tax = priceDto.getTax();
          serviceCharge = priceDto.getServiceCharge();
          discount = priceDto.getDiscount();
          commission = priceDto.getDiscount() - priceDto.getServiceCharge();
          break;
        }
      }

      var externalOrderInfo = new FlightOrderExternal();
      externalOrderInfo.setPrice(price);
      externalOrderInfo.setParvalue(parValue);
      externalOrderInfo.setTax(tax);
      externalOrderInfo.setServiceCharge(serviceCharge);
      externalOrderInfo.setDiscount(discount);
      externalOrderInfo.setOrderStatus(rs.getOrderStatus() + "");
      externalOrderInfo.setRemark(rs.getRemark());
      flightOrderDao.updateExternalOrder(enterpriseId, username, externalOrderId, externalOrderInfo);


      return JsonResult.createJsonResult(1, "同步成功, 卖家价格确认, 代理费：" + commission);
    }
  }

  private String getEotmsToken(final int enterpriseId, final int supplierId, final FlightApiService eotmsService) {
    final String key = String.format(EOTMS_TOKEN, enterpriseId, supplierId);
    if (redisDao.exists(key)) {
      return redisDao.getStr(key);
    } else {
      var rs = eotmsService.login();
      if (rs.getErrcode() == 0) {
        var token = rs.getToken();
        redisDao.setAndExpire(key, token.getBytes(UTF_8), 60L * 60 * 24);
        return token;
      }
    }
    return null;
  }

  private FlightOrderResponse doSyncEotmsFlightOrderStatus(int enterpriseId, String username, int supplierId, String externalOrderNo) throws BaseException {
    var eotmsService = createEotmsService(enterpriseId, supplierId);

    String token = getEotmsToken(enterpriseId, supplierId, eotmsService);

    if (StringUtils.isBlank(token)) {
      throw new BaseException(10004, "远端系统登录失败");
    }

    return eotmsService.getFlightOrder(token, externalOrderNo);
  }

  private JsonResult syncEotmsFlightOrderStatus(int enterpriseId, String username, int orderId) throws BaseException {
    var flightOrder = flightOrderDao.getOrderDetailById(orderId);

    var rs = doSyncEotmsFlightOrderStatus(enterpriseId, username, flightOrder.getSupplier().getId(), flightOrder.getExternalOrderNo());

    if (rs.getErrcode() == 0) {
      if (rs.getOrderStatus() == FLIGHT_ORDER_STATUS_CANCELED) {
        flightOrderDao.resetExternalOrderNo(enterpriseId, username, orderId,
                "外部订单(" + flightOrder.getExternalOrderNo() + ")已取消");
        return JsonResult.createJsonResult(-1, "外部订单已取消");
      } else {
        //校验价格
        if (rs.getPriceCheckedStatus() == 1) {
          double price = 0;
          double parValue = 0;
          double tax = 0;
          double commission = 0;
          for(var priceDto: rs.getPrices()) {
            if (priceDto.getPriceType() == PRICE_TYPE_ADULT) {
              price = priceDto.getPrice();
              parValue = priceDto.getParValue();
              tax = priceDto.getTax();
              commission = priceDto.getDiscount() - priceDto.getServiceCharge();
              break;
            }
          }
          if (flightOrder.getParvalue() != parValue) {
            return JsonResult.createJsonResult(-1, "销售价不同(" + flightOrder.getParvalue() + " != " + parValue + ")");
          }
          if (flightOrder.getTax() != tax) {
            return JsonResult.createJsonResult(-1, "税不同(" + flightOrder.getTax() + " != " + tax + ")");
          }

          FlightOrderRequestDto newDto = new FlightOrderRequestDto();
          newDto.setPsgCount(flightOrder.getPsgCount());
          newDto.setAdultCount(flightOrder.getAdultCount());

          BasePriceDto newAdultPrice = new BasePriceDto();
          newAdultPrice.setPrice(price);
          newAdultPrice.setParValue(parValue);
          newAdultPrice.setTax(tax);
          newAdultPrice.setCommission(commission);

          newDto.setAdultPrice(newAdultPrice);

          flightOrderDao.updatePrice(enterpriseId, username, orderId, newDto);

          return JsonResult.createJsonResult(1, "同步成功, 卖家价格确认, 代理费：" + commission);
        } else {
          return JsonResult.createJsonResult(-1, "卖家价格未确认");
        }
      }
    } else {
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, rs.getErrmsg());
      return JsonResult.createJsonResult(-1, rs.getErrmsg());
    }
  }

  /**
   * 执行CZ NDC 的订单查询
   * @param enterpriseId 用户企业id
   * @param username 用户名
   * @param orderNo 机票订单号
   * @param externalOrderNo 机票订单的外部订单号
   * @param bigPnr 机票订单的大编码
   * @return CZ NDC的订单结果
   */
  private OrderViewResponse execSearchNdcOrder(final int enterpriseId, final String username,
                                               final String orderNo, final String externalOrderNo, final String bigPnr) {
    var rq = new OrderRetrieveRequest();

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(enterpriseId, TMS_DEFAULT_LINK_MOBILE));

    rq.getRequest().setOrderNo(externalOrderNo);
    rq.getRequest().setPnr(bigPnr);


    //记录查询日志
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "CZ_NDC_订单查询", orderNo);
    rq.getPayloadAttributes().setTransactionId(queryId + "");

    var ndcService = createNdcService(enterpriseId);
    var rs = ndcService.searchOrder(rq);
    userDao.updateEtermCmdLog(queryId, rs.getRequestStr(), rs.getResponseStr());

    return rs;
  }

  /**
   * 处理CZ NDC订单同步结果
   * @param actMode 1 - 为“已完成”状态时，执行支付
   *                其它数值 - 不动作
   */
  private JsonResult processCzNdcOrderSyncResult(final int enterpriseId, final String username,
                                                 FlightOrder flightOrder, OrderViewResponse rs, final int actMode) {
    var jr = JsonResult.createJsonResult(1);
    final String externalOrderNo = flightOrder.getExternalOrderNo();
    final int orderId = flightOrder.getId();

    if (!SUCCESS.equalsIgnoreCase(rs.getStatus().getStatus())) {
      return JsonResult.createJsonResult(-4, rs.getStatus().getErrorMessage());
    }

    if (!externalOrderNo.equalsIgnoreCase(rs.getResponse().getOrder().getOrderNo())) {
      return JsonResult.createJsonResult(-3, "订单号不符");
    }

    final String orderStatus = rs.getResponse().getOrder().getStatus();
    final String orderStatusDesc = rs.getResponse().getOrder().getStatusDesc();

    flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, externalOrderNo + ": " + orderStatus + ", " + orderStatusDesc);

    if ("E".equalsIgnoreCase(orderStatus)) {
      //出票完成，更改本地订单状态
      //回填票号
      for (var ticket : rs.getResponse().getTickets()) {
        processCzNdcOrderTickets(flightOrder, ticket);
      }

      if (flightOrder.getStatus() != FLIGHT_ORDER_STATUS_FINISHED) {
        var ticketResult = execUpdateTicketNo(enterpriseId, username, flightOrder, 1);
        if (STATUS_OK.equalsIgnoreCase(ticketResult.getStatus())) {
          jr.setDesc("出票完成");
          //做订单完成操作
          doFinishFlightOrder(enterpriseId, username, orderId);
        }
      }
    } else if ("C".equalsIgnoreCase(orderStatus) && actMode == 1) {
      //已确认
      //已确认状态，则要判断是否执行支付操作
      this.payForFlightOrder(enterpriseId, username, orderId);
    } else if ("D".equalsIgnoreCase(orderStatus)) {
      //D 已取消
      String remark = String.format("外部订单(%s)状态为: %s(%s)", externalOrderNo, orderStatusDesc, orderStatus);
      flightOrderDao.resetExternalOrderNo(enterpriseId, username, orderId, remark);
      return JsonResult.createJsonResult(-5, remark);
    }

    return jr;
  }

  private void processCzNdcOrderTickets(FlightOrder flightOrder, TicketInfo ticket) {
    for (var psgInfo : flightOrder.getPassengers()) {
      if (psgInfo.getName().equalsIgnoreCase(ticket.getSurname())) {
        //TODO 需要考虑多张票号的情况
        if ("1".equalsIgnoreCase(ticket.getCouponSeqNumber())) {
          psgInfo.setTicketNo(ticket.getCouponNumber());
        } else {
          psgInfo.setTicketNo(psgInfo.getTicketNo() + "," + ticket.getCouponNumber());
          logger.info(psgInfo.getTicketNo());
        }
      }
    }
  }

  @Override
  public JsonResult payForFlightOrder(final int enterpriseId, final String username, final int orderId) {
    var flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "Order is not existed.");
    }

    if (flightOrder.getSupplier() == null || flightOrder.getSupplier().getIsBsp() != FLIGHT_AUTO_TICKETING_NDC_CZ) {
      return JsonResult.createJsonResult(-2, "not CZ NDC supplier.");
    }

    if (StringUtils.isBlank(flightOrder.getExternalOrderNo())) {
      return JsonResult.createJsonResult(-3, "无外部订单号");
    }
    var rq = new AirDocIssueRequest();

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_MOBILE));

    final String externalOrderNo = flightOrder.getExternalOrderNo();
    rq.getRequest().getOrder().setOrderNo(externalOrderNo);
    rq.getRequest().getOrder().setPnr(flightOrder.getBigPnrNo());
    rq.getRequest().getOrder().setTotalAmount(flightOrder.getTotalCost());

    rq.getRequest().getPayment().setPayType("BWP");
    rq.getRequest().getPayment().setTransType("01");
    rq.getRequest().getPayment().setAccountType("02");

    //记录查询日志
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "CZ_NDC_订单支付", flightOrder.getOrderNo());
    rq.getPayloadAttributes().setTransactionId(queryId + "");

    var ndcService = createNdcService(enterpriseId);
    var rs = ndcService.payForOrder(rq);
    userDao.updateEtermCmdLog(queryId, rs.getRequestStr(), rs.getResponseStr());


    if (SUCCESS.equalsIgnoreCase(rs.getStatus().getStatus())) {
      final String orderStatus = rs.getResponse().getOrder().getStatus();
      final String orderStatusDesc = rs.getResponse().getOrder().getStatusDesc();
      String remark = externalOrderNo + ": " + orderStatus + ", " + orderStatusDesc;

      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, remark);

      return JsonResult.createJsonResult(1, "支付成功");
    } else {
      //03:余额账户资金不足
      //八小时暂停
      final String key = String.format(CZ_NDC_AMOUNT_BALANCE, enterpriseId);
      redisDao.setAndExpire(key, (flightOrder.getTotalCost() + "").getBytes(UTF_8), 60L * 60 * 8);

      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, "CZ_NDC支付: " + rs.getStatus().getErrorMessage());

      final String content = String.format("机票订单(%s)使用CZ_NDC支付%f时，提示 %s.",
              flightOrder.getOrderNo(), flightOrder.getTotalCost(), rs.getStatus().getErrorMessage());
      sendCzNdcAlertToWwAppChat(enterpriseId, content);

      return JsonResult.createJsonResult(-10, rs.getStatus().getErrorMessage());
    }
  }

  @Override
  public FlightCouponInfo searchFlightOrderCoupons(AuthInfo authInfo, int orderId) {
    final int enterpriseId = authInfo.getEnterpriseId();
    final String username = authInfo.getUsername();
    var rq = new CouponShoppingRequest();

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(enterpriseId, TMS_DEFAULT_LINK_MOBILE));

    rq.getRequest().setStatus(1);
    rq.getRequest().getPagination().setStart(1);
    rq.getRequest().getPagination().setEnd(100);


//记录查询日志
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "查找优惠券", "");
    rq.getPayloadAttributes().setTransactionId(queryId + "");

    var ndcService = createNdcService(enterpriseId);
    var rs = ndcService.searchCoupon(rq);
    userDao.updateEtermCmdLog(queryId, rs.getRequestStr(), rs.getResponseStr());


    return convertCouponShopping(rs);
  }

  @Override
  @Secured({RoleInfo.ROLE_ADMIN, RoleInfo.ROLE_OPERATOR, RoleInfo.ROLE_TICKETER, RoleInfo.ROLE_SUPERVISOR})
  public JsonResult updateFlightOrderProperty(int enterpriseId, String username, int orderId, String propertyName, String propertyValue) {
    int retCode = flightOrderDao.updateFlightOrderProperty(enterpriseId, username, orderId, propertyName, propertyValue);
    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public FlightRefundFee queryOrderRefundFee(int enterpriseId, String username, int orderId) {
    var result = new FlightRefundFee();

    var flightOrder = flightOrderDao.getOrderDetailById(orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      result.setErrcode(404);
      result.setErrmsg("订单不存在");
      return result;
    }

    if (StringUtils.isBlank(flightOrder.getExternalOrderNo())) {
      result.setErrcode(400);
      result.setErrmsg("没有外部订单号");
      return result;
    }

    var rq = new OrderRefundQueryRequest();

    //大客户代码
    if (StringUtils.isNotEmpty(flightOrder.getKeyCustomerNo())) {
      rq.getPayloadAttributes().setSystem("B2G");
    }

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_MOBILE));


    for (var psg : flightOrder.getPassengers()) {
      Pax pax = new Pax();
      pax.setSurname(psg.getName());
      pax.setTicketNumber(psg.getTicketNo());
      rq.getRequest().getPassengers().add(pax);
    }

    //记录查询日志
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "CZ_NDC_退票费查询", flightOrder.getOrderNo());
    rq.getPayloadAttributes().setTransactionId(queryId + "");

    var ndcService = createNdcService(enterpriseId);
    var rs = ndcService.queryOrderRefundFee(rq);
    userDao.updateEtermCmdLog(queryId, rs.getRequestStr(), rs.getResponseStr());

    return convertFlightRefundFee(rs);
  }

  private FlightRefundFee convertFlightRefundFee(OrderRefundQueryResponse rs) {
    var result = new FlightRefundFee();
    if (!SUCCESS.equalsIgnoreCase(rs.getStatus().getStatus())) {
      result.setErrcode(100);
      result.setErrmsg(rs.getStatus().getErrorCode() + ": " + rs.getStatus().getErrorMessage());
      return result;
    }

    result.setTotalAmount(rs.getResponse().getTotalAmount());
    result.setSurcharge(rs.getResponse().getSurcharge());
    for (var f : rs.getResponse().getFees()) {
      var feeDto = new CarrierFeeDto();
      result.getFees().add(feeDto);

      feeDto.setVoluntary(f.getVoluntary());
      feeDto.setOfferId(f.getOfferId());
      feeDto.setAmount(f.getAmount());

      if (f.getFareRule() != null) {
        feeDto.setRefundFareRule(f.getFareRule().getCategories().get(0).getText().toString());
      }
    }


    return result;
  }

  @Override
  public JsonResult updateFlightOrderSupplierType(int enterpriseId, String username, int orderId, int supplierType) {
    return this.doUpdateFlightOrderSupplierType(enterpriseId, username, orderId, supplierType);
  }

  @Override
  public CommonDto<FlightInfoDto> queryOrderChange(final int enterpriseId, final String username, final int orderId,
                                                   List<FlightSearchCriteria> flightSearchCriteria) {
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);
    var rq = new OrderChangeQueryRequest();

    rq.getMemberInfo().setAccountNumber(doGetEntCfg(flightOrder.getEnterpriseId(), TMS_DEFAULT_LINK_MOBILE));

    rq.getRequest().setOrderNo(flightOrder.getExternalOrderNo());
    rq.getRequest().setDirectOnly(false);

    for (var sci : flightSearchCriteria) {
      for (var flt : flightOrder.getFlights()) {
        if (flt.getFlight().getDport().equalsIgnoreCase(sci.getDport()) && flt.getFlight().getAport().equalsIgnoreCase(sci.getAport())) {
          var originDestRequest = new OriginDestRequest();
          originDestRequest.setOrigCityCode(sci.getDport());
          originDestRequest.setDestCityCode(sci.getAport());
          originDestRequest.setTravelDate(sci.getDdate());
          rq.getRequest().getNewOriginDestinations().add(originDestRequest);

          originDestRequest = new OriginDestRequest();
          originDestRequest.setOrigCityCode(flt.getFlight().getDport());
          originDestRequest.setDestCityCode(flt.getFlight().getAport());
          try {
            originDestRequest.setTravelDate(DateUtil.convertToDate(flt.getFlight().getDdate(), DATE_YYYY_MM_DD));
          } catch (ParseException e) {
            e.printStackTrace();
          }
          rq.getRequest().getOriginDestinations().add(originDestRequest);

          break;
        }
      }
    }

    for (var psg : flightOrder.getPassengers()) {
      TicketPassengerInfo tpi = new TicketPassengerInfo();
      tpi.setTicketNo(psg.getTicketNo());
      tpi.setPassengerName(psg.getName());
      rq.getRequest().getTicketPassengers().add(tpi);
    }

    var paxQuery = new PaxQuery();
    paxQuery.setPassengerType("ADT");
    paxQuery.setPassengerNumber(flightOrder.getPassengers().size());
    rq.getRequest().getTravelers().add(paxQuery);


//    记录查询日志
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "CZ_NDC_改签航班查询", flightOrder.getOrderNo());
    rq.getPayloadAttributes().setTransactionId(queryId + "");

    var ndcService = createNdcService(enterpriseId);
    var rs = ndcService.searchOrderChangeQuery(rq);
    userDao.updateEtermCmdLog(queryId, rs.getRequestStr(), rs.getResponseStr());


    CommonDto<FlightInfoDto> results = new CommonDto<>();

    if (SUCCESS.equalsIgnoreCase(rs.getStatus().getStatus())) {
      convertBaseShoppingResponse(rs.getResponse(), results);
    } else {
      results.setErrcode(10000);
      results.setErrmsg(rs.getStatus().getErrorCode() + ": " + rs.getStatus().getErrorMessage());
    }

    return results;

  }

  @Override
  public void appendFlightOrderLog(int enterpriseId, String username, int orderId, String remark) {
    flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, remark);
  }

  @Override
  public JsonResult createExternalFlightOrder(final int enterpriseId, final String username, final int orderId,
                                              final int supplierId) throws BaseException {
    if (supplierId < 1) {
      return JsonResult.createJsonResult(-1, "请选择供应商");
    }
    var flightOrder = flightOrderDao.getOrderDetailById(orderId);

    var rq = new FlightOrderCreateRequest();
    for(var psg0: flightOrder.getPassengers()) {
      if (psg0.getPsgType() != PSG_TYPE_ADU) {
        return JsonResult.createJsonResult(-1, "暂不支持非成人订单");
      }
      var psg = new BasePassengerDto();

      psg.setName(psg0.getName());
      psg.setPsgType(psg0.getPsgType());
      psg.setIdType(psg0.getIdType());
      psg.setIdNo(psg0.getIdNo());
      psg.setMobile(psg0.getMobile());

      rq.getPassengers().add(psg);
    }

    //获取该供应商的航司限制信息
    var airlinesLimit = StringUtils.trimToEmpty(getEotmsSupplierAirlinesLimit(enterpriseId, supplierId));

    for(var flt0: flightOrder.getFlights()) {
      var flt = new FlightInfoDto();

      var twoCode = flt0.getFlight().getFlightNo().substring(0, 2);
      if (airlinesLimit.length() > 0 && !airlinesLimit.contains(twoCode)) {
        return JsonResult.createJsonResult(-4, "该供应商只允许开指定航司(" + airlinesLimit + ")");
      }

      flt.setFlightNo(flt0.getFlight().getFlightNo());
      flt.setSubclass(flt0.getFlight().getSubclass());
      flt.setDport(flt0.getFlight().getDport());
      flt.setAport(flt0.getFlight().getAport());
      try {
        flt.setDdate(DateUtil.convertToDate(flt0.getFlight().getDdate(), DATE_YYYY_MM_DD));
      } catch (ParseException e) {
        logger.error(e.getMessage());
      }
      flt.setDtime(flt0.getFlight().getDtime());
      flt.setAtime(flt0.getFlight().getAtime());

      rq.getFlights().add(flt);
    }



    var price = new BasePriceDto();
    rq.getPrices().add(price);
    price.setPriceType(PRICE_TYPE_ADULT);
    price.setParValue(flightOrder.getParvalue());
    price.setTax(flightOrder.getTax());

    rq.setTotalAmount(flightOrder.getTotalAmount());

    var eotmsService = createEotmsService(enterpriseId, supplierId);
    if (eotmsService == null) {
      return JsonResult.createJsonResult(-1, "请联系管理员配置相关参数");
    }

    var supplier = enterpriseDao.getSupplierById(supplierId);
    if (supplier == null || (supplier.getEnterpriseId() != 0 && supplier.getEnterpriseId() != enterpriseId)) {
      throw new BaseException(10000, "未找到指定供应商");
    }

    //校验是否已向该供应商提交过
    var existedOrders = flightOrderDao.searchExternalFlightOrders(enterpriseId, username, orderId);
    if (!existedOrders.isEmpty()) {
      return JsonResult.createJsonResult(-1, "已存在" + existedOrders.size() + "个该供应商的外部订单");
    }

    //token
    String token = getEotmsToken(enterpriseId, supplierId, eotmsService);
    if (StringUtils.isBlank(token)) {
      return JsonResult.createJsonResult(-2, "远端系统登录失败");
    }

    var rs = eotmsService.createFlightOrder(token, rq);
    if (rs.getErrcode() == 0) {
      //远端系统订单生成成功
      flightOrderDao.saveExternalOrder(enterpriseId, flightOrder.getOrderNo(), supplierId, rs.getOrderNo());
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, "外部订单创建成功(" + rs.getOrderNo() + ", " + supplier.getName() + ")");
      return JsonResult.createJsonResult(1, "外部订单号：" + rs.getOrderNo());
    } else {
      return JsonResult.createJsonResult(-1, rs.getErrmsg());
    }
  }

  private String getEotmsSupplierAirlinesLimit(int enterpriseId, int supplierId) {
    var cfg = getEntEotmsConfig(enterpriseId, supplierId);
    return cfg == null ? null : cfg.getAirlineLimit();
  }

  @Override
  public JsonResult confirmExternalFlightOrder(final int enterpriseId, final String username, final int externalOrderId) {
    var externalOrderInfo = flightOrderDao.getExternalOrder(externalOrderId);
    if (externalOrderInfo == null || externalOrderInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "未找到此外部订单");
    }
    //外部订单号
    final String externalOrderNo = externalOrderInfo.getExternalOrderNo();

    if (externalOrderInfo.getParvalue() == null) {
      return JsonResult.createJsonResult(-1, "请先同步外部订单信息");
    }

    final int supplierId = externalOrderInfo.getSupplierId();

    var flightOrder = flightOrderDao.getOrderDetailByOrderNo(enterpriseId, externalOrderInfo.getOrderNo());
    if (flightOrder == null) {
      return JsonResult.createJsonResult(-1, "未找到订单");
    }

    if (StringUtils.isNotBlank(flightOrder.getExternalOrderNo())
            && !flightOrder.getExternalOrderNo().equalsIgnoreCase(externalOrderNo)) {
      return JsonResult.createJsonResult(-3, "该机票订单已绑定其它外部订单号");
    }

    if (flightOrder.getSupplier() == null) {
      //设置供应商
      flightOrderDao.updateSupplier(enterpriseId, username, flightOrder.getId(), supplierId);
    } else if (flightOrder.getSupplier().getId() != externalOrderInfo.getSupplierId()) {
      return JsonResult.createJsonResult(-4, "该机票订单已绑定供应商不匹配");
    }

    if (flightOrder.getSupplier().getEnterpriseId() == 0) {
      //平台供应商
      //判断当前用户的余额是否足够
      var bal = enterpriseDao.getEnterpriseBalance(enterpriseId);
      var availableAmount = bal.getCashBalance() + bal.getCreditLimit() - bal.getFrozenBalance();
      var fee = (int)(flightOrder.getTotalAmount() * 100);
      if (availableAmount < fee) {
        return JsonResult.createJsonResult(-2, "预付金额不足");
      }
    }

    var eotmsService = createEotmsService(enterpriseId, supplierId);
    if (eotmsService == null) {
      return JsonResult.createJsonResult(-1, "请先配置参数");
    }

    //token
    String token = getEotmsToken(enterpriseId, supplierId, eotmsService);
    if (StringUtils.isBlank(token)) {
      return JsonResult.createJsonResult(-2, "远端系统登录失败");
    }

    //设置外部订单号
    if (StringUtils.isBlank(flightOrder.getExternalOrderNo())) {
      double parvalue = externalOrderInfo.getParvalue();
      double price = externalOrderInfo.getPrice();
      double tax = externalOrderInfo.getTax();
      double serviceCharge = externalOrderInfo.getServiceCharge();
      double discount = externalOrderInfo.getDiscount();
      if (flightOrder.getParvalue() != parvalue) {
        return JsonResult.createJsonResult(-1, "销售价不同(" + flightOrder.getParvalue() + " != " + parvalue + ")");
      }
      if (flightOrder.getTax() != tax) {
        return JsonResult.createJsonResult(-1, "税不同(" + flightOrder.getTax() + " != " + tax + ")");
      }

      flightOrderDao.updateExternalOrderNo(enterpriseId, username, flightOrder.getId(), externalOrderNo, null, "");

      FlightOrderRequestDto newDto = new FlightOrderRequestDto();
      newDto.setPsgCount(flightOrder.getPsgCount());
      newDto.setAdultCount(flightOrder.getAdultCount());

      BasePriceDto newAdultPrice = new BasePriceDto();
      newAdultPrice.setPrice(price);
      newAdultPrice.setParValue(parvalue);
      newAdultPrice.setTax(tax);
      newAdultPrice.setCommission(discount - serviceCharge);

      newDto.setAdultPrice(newAdultPrice);

      flightOrderDao.updatePrice(enterpriseId, username, flightOrder.getId(), newDto);
    }

    var rs = eotmsService.getFlightOrder(token, externalOrderNo);
    if (rs.getErrcode() == 0) {
      //检查外部订单状态，提交出票
      var rs1 = eotmsService.submitFlightOrder(token, rs.getId());
      if (rs1.getErrcode() == 0) {
        return JsonResult.createJsonResult(1, "提交开票成功");
      } else {
        return JsonResult.createJsonResult(-1, rs1.getErrmsg());
      }
    } else {
      return JsonResult.createJsonResult(-1, rs.getErrmsg());
    }
  }

  @Override
  public List<FlightExternalOrderDto> searchExternalFlightOrders(int enterpriseId, String username, int orderId) {
    var srcList =  flightOrderDao.searchExternalFlightOrders(enterpriseId, username, orderId);
    var list = new ArrayList<FlightExternalOrderDto>();
    for(var item: srcList) {
      var dto = new FlightExternalOrderDto();
      BeanUtils.copyProperties(item, dto);

      dto.calc();

      list.add(dto);
    }

    return list;
  }

  private FlightCouponInfo convertCouponShopping(CouponShoppingResponse rs) {
    var fc = new FlightCouponInfo();
    for (var couponDetail : rs.getResponse().getCouponDetails()) {
      var fci = new FlightCouponItem();
      fc.getItems().add(fci);

      fci.setGroupId(couponDetail.getGroupId());
      fci.setFaceValue(couponDetail.getFaceValue());
      fci.setQuantity(couponDetail.getQuantity());
      fci.setTotalAmount(couponDetail.getTotalAmount());

      for (var rule : rs.getResponse().getCouponRules()) {
        if (rule.getGroupId().equalsIgnoreCase(fci.getGroupId())) {

          var itemRule = new FlightCouponItemRule();
          fci.setRuleInfo(itemRule);

          itemRule.setGroupId(rule.getGroupId());

          itemRule.setCabin(rule.getCabin());
          itemRule.setCouponType(rule.getCouponType());
          itemRule.setSegmentType(rule.getSegmentType());
          itemRule.setFlightTimeType(rule.getFlightTimeType());

          try {
            itemRule.setFlightStartDate(DateUtil.convertToDate(rule.getFlightStartDate(), DATE_YYYY_MM_DD));
            itemRule.setFlightEndDate(DateUtil.convertToDate(rule.getFlightEndDate(), DATE_YYYY_MM_DD));
            itemRule.setUsefulStartDate(DateUtil.convertToDate(rule.getUsefulStartDate(), DATE_YYYY_MM_DD));
            itemRule.setUsefulEndDate(DateUtil.convertToDate(rule.getFlightEndDate(), DATE_YYYY_MM_DD));
          } catch (ParseException e) {
            e.printStackTrace();
          }

          break;
        }
      }
    }
    return fc;
  }

  private void adjustFlightOrderPrice(FlightOrderRequestDto dto, final int psgType, List<String> offerItemIds,
                                      FlightOfferPrice rs0, CustomerServiceTemplateDto serviceTemplate) {
    final String passengerType = psgType == PSG_TYPE_ADU ? "ADT" : "CHD";
    double baseAmount = 0;
    double totalTax = 0;
    double totalFee = 0;
    for (var itemId : offerItemIds) {
      for (var seg : rs0.getSegments()) {
        for (var fi : seg.getFareInfoDtos()) {
          if (fi.getOfferItemId().equalsIgnoreCase(itemId) && passengerType.equalsIgnoreCase(fi.getPsgType())) {
            baseAmount += fi.getBaseAmount();
            totalTax += fi.getTotalTax();
            totalFee += fi.getTotalFee();
          }
        }
      }
    }

    BasePriceDto priceDto = psgType == PSG_TYPE_ADU ? dto.getAdultPrice() : dto.getChildPrice();

    priceDto.setCommission(totalFee);
    priceDto.setTax(totalTax);
    if (priceDto.getPrice() != baseAmount) {
      logger.error("{} != {}", priceDto.getPrice(), baseAmount);
      priceDto.setPrice(baseAmount);
    }
    if (priceDto.getParValue() < 0.01) {
      priceDto.setParValue(baseAmount);
    }

    if (serviceTemplate == null) {
      return;
    }

    //出票服务费
    priceDto.setServiceCharge(serviceTemplate.getFlightTicketingFee());
    if (serviceTemplate.getFlightPriceMode() == 2) {
      //底价（代理费全返）
      priceDto.setDiscount(totalFee);
    }
  }

  private void generatePassengersFromExcel(List<PassengerDto> list, Workbook wb) {
    Sheet sheet = wb.getSheetAt(0);
    final int rowNum = sheet.getLastRowNum() + 1;

    for (int i = 1; i < rowNum; i++) {
      Row row = sheet.getRow(i);
      if (row == null) {
        continue;
      }

      PassengerDto passengerDto = new PassengerDto();
      passengerDto.setName(getStringCellValue(row.getCell(0)));

      //证件号码
      String temp = getStringCellValue(row.getCell(1));
      passengerDto.setIdNo(temp);

      //手机号
      temp = getStringCellValue(row.getCell(2));
      passengerDto.setMobile(temp);

      //证件类型
      temp = getStringCellValue(row.getCell(3));
      if (StringUtils.isEmpty(temp) || "身份证".equalsIgnoreCase(temp)) {
        passengerDto.setIdType(ID_TYPE_NI);
      } else if ("护照".equalsIgnoreCase(temp)) {
        passengerDto.setIdType(ID_TYPE_PASSPORT);
      } else {
        passengerDto.setIdType(ID_TYPE_OTHER);
      }

      //乘客类型
      temp = getStringCellValue(row.getCell(4));
      if (StringUtils.isEmpty(temp) || "成人".equalsIgnoreCase(temp)) {
        passengerDto.setPsgType(PSG_TYPE_ADU);
      } else if ("儿童".equalsIgnoreCase(temp)) {
        passengerDto.setPsgType(PSG_TYPE_CHD);
      } else {
        passengerDto.setPsgType(PSG_TYPE_INF);
      }


      if (StringUtils.isNotEmpty(passengerDto.getName())) {
        list.add(passengerDto);
      }
    }

  }




  private int checkFlightOrderTicketIsFinalStatus(final int enterpriseId, final String username, FlightOrderResponseDto detail) {
    //是否到了最终状态，默认为1（是）
    int finalStatus = 1;

    for (BaseFlightOrderPassengerDto psg : detail.getPassengers()) {
      if (psg.getTicketStatus() == FLIGHT_TICKET_STATUS_USED
              || psg.getTicketStatus() == FLIGHT_TICKET_STATUS_REFUNDED
              || psg.getTicketStatus() == FLIGHT_TICKET_STATUS_EXCHANGED
              || psg.getTicketStatus() == FLIGHT_TICKET_STATUS_VOID
      ) {
        continue;
      }

      finalStatus = 0;
    }

    if (finalStatus == 1) {
      //已为终态，不需要再检查DETR内容
      flightOrderDao.updateFlightOrderDetrStatus(enterpriseId, username, detail.getId(), 8, "Final");
    }

    return finalStatus;
  }


  private int convertCabin(String cabinClass) {
    /*
      舱位性质
      10-头等舱
      20-商务舱
      30-经济舱
      40-超级经济舱
     */
    switch (cabinClass) {
      case "F":
        return 10;
      case "C":
        return 20;
      case "Y":
        return 30;
      default:
        return 100;
    }
  }

  /**
   * 计算飞行时长，分钟
   */
  private int calcFlyTime(String ddate, String adate, String dtime, String atime) {
    int flyTime = Integer.parseInt(dtime.substring(0, 2)) * 60 + Integer.parseInt(dtime.substring(2, 4));
    flyTime -= Integer.parseInt(atime.substring(0, 2)) * 60 + Integer.parseInt(atime.substring(2, 4));

    if (!ddate.equalsIgnoreCase(adate)) {
      //建议计算
      flyTime += 24 * 60;
    }

    return flyTime;
  }

  /**
   * 为机票订单指定审批人
   */
  private void assignApproverFlightOrder(final int enterpriseId, final String username, final int orderId, final String approver) {
    //TODO 发送短信或邮件提醒该审批人
    flightOrderDao.updateFlightOrderApprover(enterpriseId, username, orderId, approver);
    //发送通知给指定的审批人
  }

}
